{"version":3,"sources":["../src/index.ts","../src/lib/environment.ts","../src/lib/remote-config.ts","../src/lib/version.ts","../src/lib/runtime-log.ts","../src/lib/sentry/client.ts","../src/lib/sentry/DappUiErrorBoundary.tsx","../src/lib/utils.ts","../src/lib/sentry/user-id.ts","../src/lib/sentry/analytics.ts","../src/lib/sentry/TrackingBoundary.tsx","../src/features/app-launcher/components/AppLauncher.tsx","../src/features/app-launcher/providers/app-launcher-provider.ts","../src/hooks/use-history-dismiss.ts","../src/hooks/use-media-query.ts","#style-inject:#style-inject","../src/features/app-launcher/styles/theme.css","../src/features/app-launcher/styles/base.css","../src/features/app-launcher/styles/keyframes.css","../src/features/app-launcher/styles/component.css","../src/features/app-launcher/styles/utilities.css","../src/features/app-launcher/components/AppLauncherTrigger.tsx","../src/features/app-launcher/components/icons.tsx","../src/features/app-launcher/components/AppLauncherContent.tsx","../src/features/app-launcher/queries/use-global-menu.ts","../src/features/app-launcher/constants.ts","../src/features/app-launcher/fallback-menu.ts","../src/features/app-launcher/menu.ts","../src/features/wallet-info/components/WalletInfo.tsx","../src/features/wallet-info/providers/wallet-info-provider.ts","../src/features/wallet-info/types.ts","../src/features/send/components/SendFlow.tsx","../src/features/wallet-info/components/icons.tsx","../src/features/send/model/useSendFlow.ts","../src/lib/format.ts","../src/features/send/queries/use-chain-info.ts","../src/lib/constants.ts","../src/features/send/types.ts","../src/features/send/utils.ts","../src/features/send/model/sendFormat.ts","../src/features/send/utils/sendErrorMessage.ts","../src/features/send/utils/gas-rpc.ts","../src/features/send/model/SendRepository.ts","../src/features/send/model/sendUseCases.ts","../src/features/send/components/steps/SendAmount.tsx","../src/features/send/components/steps/SendRecipient.tsx","../src/features/wallet-info/constants.ts","../src/features/send/components/icons.tsx","../src/features/send/components/steps/SendResult.tsx","../src/features/send/components/steps/SendReview.tsx","../src/features/send/components/steps/SendTokenSelect.tsx","../src/features/send/styles/send-flow.css","../src/features/wallet-info/components/WalletInfoTrigger.tsx","../src/features/wallet-info/components/WalletInfoContent.tsx","../src/lib/numeric.ts","../src/features/wallet-info/queries/use-token-balance.ts","../src/features/wallet-info/queries/use-forge-holdings.ts","../src/features/wallet-info/queries/use-token-stats.ts","../src/features/wallet-info/queries/use-crossd-apr.ts","../src/features/wallet-info/queries/use-token-info.ts","../src/features/wallet-info/queries/use-total-value.ts","../src/features/wallet-connect-modal/last-selected.ts","../src/features/wallet-connect-modal/constants.ts","../src/features/wallet-connect-modal/components/icons.tsx","../src/lib/outlink.ts","../src/features/wallet-portfolio/components/WalletPortfolio.tsx","../src/features/wallet-portfolio/providers/wallet-portfolio-provider.ts","../src/features/wallet-portfolio/utils.ts","../src/features/wallet-portfolio/components/WalletPortfolioTrigger.tsx","../src/features/wallet-portfolio/components/WalletPortfolioContent.tsx","../src/features/wallet-portfolio/components/WalletPortfolioBody.tsx","../src/features/wallet-portfolio/providers/section-status-provider.ts","../src/features/wallet-portfolio/components/icons.tsx","../src/features/wallet-portfolio/queries/use-reward-pools.ts","../src/features/wallet-portfolio/queries/use-user-deposits.ts","../src/features/wallet-portfolio/queries/use-token-info.ts","../src/features/wallet-portfolio/constants.ts","../src/features/wallet-portfolio/components/sections/SectionCard.tsx","../src/features/wallet-portfolio/components/sections/RewardsSection.tsx","../src/features/wallet-portfolio/components/sections/PointsSection.tsx","../src/features/wallet-portfolio/queries/use-withdraw-positions.ts","../src/features/wallet-portfolio/queries/lp-balance-rpc-fallback.ts","../src/features/wallet-portfolio/components/sections/WithdrawDialog.tsx","../src/features/wallet-portfolio/components/sections/useWithdrawFlow.ts","../src/features/wallet-portfolio/queries/use-reward-factory-info.ts","../src/features/wallet-portfolio/refresh.ts","../src/features/wallet-portfolio/components/sections/StakeSection.tsx","../src/features/wallet-portfolio/queries/use-stake-info.ts","../src/features/wallet-portfolio/queries/use-stake-rewards.ts","../src/features/wallet-portfolio/queries/use-staking-onchain.ts","../src/features/wallet-portfolio/queries/use-cross-usd-price.ts","../src/features/wallet-portfolio/components/sections/GameSwapSection.tsx","../src/features/wallet-portfolio/queries/use-game-swap-pools.ts","../src/features/wallet-portfolio/queries/use-game-swap-lp-balances.ts","../src/features/wallet-portfolio/components/sections/ForgeSection.tsx","../src/features/wallet-portfolio/queries/use-forge-pools.ts","../src/features/wallet-portfolio/queries/use-forge-token-details.ts","../src/features/wallet-portfolio/components/sections/CrossdPoolSection.tsx","../src/features/wallet-portfolio/queries/use-crossd-positions.ts","../src/features/wallet-portfolio/queries/use-crossd-position-details.ts","../src/features/wallet-portfolio/components/sections/CrossdWithdrawDialog.tsx","../src/features/wallet-portfolio/components/sections/useCrossdWithdrawFlow.ts","../src/features/wallet-portfolio/v3-position-encoding.ts","../src/features/wallet-portfolio/queries/v3-simulation-rpc.ts","../src/features/wallet-portfolio/types.ts","../src/features/wallet-portfolio/styles/portfolio.css","../src/features/bridge/components/BridgeFlow.tsx","../src/hooks/use-soft-keyboard-open.ts","../src/hooks/use-exit-delay.ts","../src/features/bridge/components/icons.tsx","../src/features/bridge/model/useBridgeFlow.ts","../src/features/bridge/model/BridgeRepository.ts","../src/features/bridge/styles/bridge-flow.css","../src/features/wallet-info/components/TokenDetailView.tsx","../src/features/wallet-info/queries/use-token-history.ts","../src/features/wallet-info/queries/use-token-detail-visibility.ts","../src/features/wallet-info/components/TokenPriceChart.tsx","../src/features/wallet-info/queries/use-token-price-history.ts","../src/features/onepop/components/OnePopBody.tsx","../src/features/onepop/landing.ts","../src/features/onepop/components/icons.tsx","../src/features/onepop/styles/onepop.css","../src/features/onepop/components/OnePopGetTokenTrigger.tsx","../src/features/wallet-info/components/WalletInfoNav.tsx","../src/features/wallet-info/components/WalletInfoFooter.tsx","../src/features/wallet-info/styles/theme.css","../src/features/wallet-info/styles/base.css","../src/features/wallet-info/styles/keyframes.css","../src/features/wallet-info/styles/component.css","../src/features/wallet-info/styles/token-detail.css","../src/features/wallet-info/styles/utilities.css","../src/features/get-one-usd/components/GetOneUsd.tsx","../src/features/relay/components/RelayDeposit.tsx","../src/features/relay/providers/relay-deposit-provider.ts","../src/features/relay/components/RelayDepositContent.tsx","../src/features/relay/providers/relay-shell-provider.tsx","../src/features/relay/providers/relay-wizard-provider.tsx","../src/features/relay/styles/component.css","../src/features/relay/components/DepositChecklist.tsx","../src/features/relay/components/DepositStepView.tsx","../src/features/relay/lib/units.ts","../src/features/relay/components/icons.tsx","../src/features/relay/lib/token-logos.ts","../src/features/relay/components/Dropdown.tsx","../src/features/relay/components/QuotePreview.tsx","../src/features/relay/components/SourceForm.tsx","../src/features/relay/lib/explorer.ts","../src/features/relay/components/DepositPanel.tsx","../src/features/relay/components/OrdersList.tsx","../src/features/relay/components/RecoverFunds.tsx","../src/features/relay/hooks/useRecovery.ts","../src/features/relay/lib/client.ts","../src/features/relay/lib/abis.ts","../src/features/relay/lib/verifyForwarder.ts","../src/features/relay/lib/recoveryGroups.ts","../src/features/relay/components/shell/ResponsiveShell.tsx","../src/features/relay/styles/shell.css","../src/features/relay/components/RelayDepositTrigger.tsx","../src/features/relay/components/RelayWizardStateHost.tsx","../src/features/relay/hooks/useDepositAddress.ts","../src/features/relay/hooks/useDebouncedValue.ts","../src/features/relay/hooks/useRelayOrders.ts","../src/features/relay/hooks/useTargetTokens.ts","../src/features/relay/lib/api-base.ts","../src/features/relay/providers/relay-history-provider.ts","../src/features/relay/components/RelayHistoryContent.tsx","../src/features/relay/components/RelayHistoryTrigger.tsx","../src/features/relay/components/RelayHistory.tsx","../src/features/relay/components/RelayRecovery.tsx","../src/features/relay/hooks/useRecoverableCount.ts","../src/features/relay/providers/relay-recovery-provider.ts","../src/features/relay/components/RelayRecoveryContent.tsx","../src/features/relay/components/RelayRecoveryTrigger.tsx","../src/features/relay/hooks/useRelayConfig.ts","../src/features/relay/hooks/useRelayRecovery.ts","../src/features/relay/components/StatusTracker.tsx","../src/features/get-one-usd/lib/token-refs.ts","../src/lib/version-gate.ts","../src/features/get-one-usd/lib/action-modes.ts","../src/features/get-one-usd/lib/one-usd-site.ts","../src/features/get-one-usd/hooks/use-get-one-usd-apr.ts","../src/features/get-one-usd/lib/transaction-status.ts","../src/features/get-one-usd/components/icons.tsx","../src/features/get-one-usd/styles/component.css","../src/features/wallet-connect-modal/components/WalletConnectModal.tsx","../src/features/wallet-connect-modal/providers/wallet-connect-modal-provider.ts","../src/features/wallet-connect-modal/hooks/use-wallet-detect.ts","../src/features/wallet-connect-modal/components/WalletConnectModalTrigger.tsx","../src/features/wallet-connect-modal/components/WalletConnectModalContent.tsx","../src/features/wallet-connect-modal/styles/component.css","../src/features/wallet-connect-modal/styles/keyframes.css","../src/features/connect-button/components/ConnectButton.tsx","../src/features/connect-button/styles/theme.css","../src/features/connect-button/styles/component.css","../src/features/connect-button/icons.ts","../src/features/skills/components/SkillsButton.tsx","../src/features/skills/styles/theme.css","../src/features/skills/styles/component.css"],"sourcesContent":["export type { Environment, Theme, DrawerDirection } from \"./types\";\nexport { resolveEnvironment } from \"./lib/environment\";\nexport {\n  useChainDisplay,\n  useChainsConfig,\n  getChainDisplay,\n  CHAINS_CONFIG_FILE,\n} from \"./lib/remote-config\";\nexport type { ChainDisplayMeta, ChainsConfig } from \"./lib/remote-config\";\nexport { announceAppLauncherUsage } from \"./lib/runtime-log\";\nexport type { AppLauncherUsageMode } from \"./lib/runtime-log\";\nexport {\n  initDappUiSentry,\n  getDappUiSentryScope,\n  captureDappUiException,\n  DappUiErrorBoundary,\n  trackDappUiEvent,\n  trackDappUiFunnel,\n  setDappUiAnalyticsUser,\n  normalizeFailureReason,\n} from \"./lib/sentry/index\";\nexport type {\n  InitDappUiSentryOptions,\n  DappUiErrorBoundaryProps,\n  DappUiFeature,\n  DappUiFlow,\n  DappUiFailureReason,\n  TrackDappUiFunnelOptions,\n} from \"./lib/sentry/index\";\n\nimport { announceAppLauncherUsage } from \"./lib/runtime-log\";\n\nannounceAppLauncherUsage({ mode: \"dapp-ui\" });\n\nexport {\n  AppLauncher,\n  AppLauncherTrigger,\n  AppLauncherContent,\n  useGlobalMenu,\n} from \"./features/app-launcher/index\";\nexport type {\n  AppLauncherProps,\n  AppLauncherTriggerProps,\n  AppLauncherTriggerStyle,\n  AppLauncherContentProps,\n  GlobalMenu,\n  GlobalMenuItemAssetUrl,\n  GlobalMenuItem,\n  GlobalMenuItemUrl,\n  GlobalMenuItemServiceStatus,\n} from \"./features/app-launcher/index\";\n\nexport {\n  WalletInfo,\n  ConnectorId,\n  CONNECTOR_REGISTRY,\n  useTokenBalance,\n  USER_BALANCE_QUERY_KEY,\n  useTokenStats,\n  TOKEN_STATS_QUERY_KEY,\n} from \"./features/wallet-info/index\";\nexport type {\n  ChainId,\n  PreferredToken,\n  ConnectorMeta,\n  WalletInfoProps,\n  WalletInfoStyle,\n  WalletInfoTriggerProps,\n  WalletInfoContentProps,\n  WalletInfoNavProps,\n  WalletInfoFooterProps,\n  TokenBalance,\n  TokenBalanceResponse,\n  TokenStats,\n  TokenStatsResponse,\n} from \"./features/wallet-info/index\";\n\nexport {\n  WalletPortfolio,\n  WalletPortfolioBody,\n  PORTFOLIO_SECTIONS,\n  WALLET_PORTFOLIO_QUERY_PREFIX,\n  invalidateWalletPortfolioQueries,\n} from \"./features/wallet-portfolio/index\";\nexport type {\n  PortfolioSection,\n  WalletPortfolioProps,\n  WalletPortfolioTriggerProps,\n  WalletPortfolioContentProps,\n  WalletPortfolioBodyProps,\n  EstimateGasArgs,\n  EstimateGasFn,\n  GasEstimate,\n  GetTransactionReceiptArgs,\n  GetTransactionReceiptFn,\n  SendTransactionArgs,\n  SendTransactionFn,\n  TransactionReceiptResult,\n  LpBalanceInfo,\n  LpBalanceReaderFn,\n  StakingRewardsInfo,\n  StakingRewardsReaderFn,\n  GameSwapPool,\n  GameSwapTokenRef,\n  CrossdPosition,\n  CrossdPositionPoolRef,\n  CrossdPositionTokenRef,\n  CrossdPositionDetail,\n} from \"./features/wallet-portfolio/index\";\n\nexport { SendFlow } from \"./features/send/index\";\nexport type {\n  SendFlowProps,\n  RecentSendAddress,\n  SendAccount,\n  SendPageProps,\n  SendAsset,\n  SendStatus,\n} from \"./features/send/index\";\n\nexport {\n  OnePopBody,\n  OnePopGetTokenTrigger,\n} from \"./features/onepop/index\";\nexport type {\n  OnePopBodyProps,\n  OnePopActivityDirection,\n  OnePopActivityItem,\n  OnePopActivityStatus,\n  OnePopSummary,\n} from \"./features/onepop/index\";\n\nexport { BridgeFlow } from \"./features/bridge/index\";\nexport type {\n  BridgeAmountSource,\n  BridgeApprovalInfo,\n  BridgeApproveFn,\n  BridgeFailedInfo,\n  BridgeGetApprovalFn,\n  BridgeFlowProps,\n  BridgeGetToTokensFn,\n  BridgeHistoryItem,\n  BridgeInfoRow,\n  BridgeInfoTokenRef,\n  BridgeLiquidityInfo,\n  BridgePathType,\n  BridgeQuoteFn,\n  BridgeQuoteInput,\n  BridgeQuoteResult,\n  BridgeStatus,\n  BridgeStep,\n  BridgeSubmitFn,\n  BridgeSubmittedInfo,\n  BridgeToken,\n  BridgeTxSummary,\n} from \"./features/bridge/index\";\n\nexport {\n  GetOneUsd,\n  findGetOneUsdToken,\n  getOneUsdTokenKey,\n  isGetOneUsdActionEnabled,\n  isGetOneUsdActionSupported,\n  isGetOneUsdActionVisible,\n  isGetOneUsdTargetAvailable,\n  matchesGetOneUsdToken,\n  resolveGetOneUsdRoute,\n} from \"./features/get-one-usd/index\";\nexport type {\n  GetOneUsdActionId,\n  GetOneUsdBridgeProps,\n  GetOneUsdButtonComponent,\n  GetOneUsdEvent,\n  GetOneUsdMode,\n  GetOneUsdRemoteModes,\n  GetOneUsdPairs,\n  GetOneUsdProps,\n  GetOneUsdRelayProps,\n  GetOneUsdTokenRef,\n  GetOneUsdWaitForTransaction,\n  ResolvedGetOneUsdRoute,\n} from \"./features/get-one-usd/index\";\n\nexport type {\n  OnOutlink,\n  OutlinkContext,\n  OutlinkCategory,\n  OutlinkOrigin,\n} from \"./lib/outlink\";\n\nexport {\n  WalletConnectModal,\n  WALLET_REGISTRY,\n  SOCIAL_REGISTRY,\n  useWalletDetect,\n} from \"./features/wallet-connect-modal/index\";\nexport type {\n  WalletConnectModalProps,\n  WalletConnectModalTriggerProps,\n  WalletConnectModalContentProps,\n  WalletConnectModalStyle,\n  WalletHandlers,\n  WalletConfig,\n  WalletId,\n  SocialHandlers,\n  SocialConfig,\n  SocialId,\n} from \"./features/wallet-connect-modal/index\";\n\nexport {\n  ConnectButton,\n  APPLE_ICON,\n  BINANCE_ICON,\n  CROSSX_ICON,\n  GOOGLE_ICON,\n  METAMASK_ICON,\n} from \"./features/connect-button/index\";\nexport type {\n  ConnectButtonProps,\n  ConnectButtonStyle,\n  WalletProvider,\n} from \"./features/connect-button/index\";\n\nexport { SkillsButton, DEFAULT_SKILLS_HREF } from \"./features/skills/index\";\nexport type { SkillsButtonProps, SkillsButtonStyle } from \"./features/skills/index\";\n\nexport {\n  RelayDeposit,\n  RelayHistory,\n  RelayRecovery,\n  useRelayConfig,\n  useRelayOrders,\n  useRelayRecovery,\n  // Presentational single-order stepper -- not used by the deposit wizard, but\n  // exported for hosts polling GET /v1/orders/{id} themselves. No name clash at\n  // this barrel, so it needs no relay-specific alias.\n  StatusTracker,\n  createRelayClient,\n  RelayApiError,\n  DEFAULT_TRUSTED_FACTORIES,\n} from \"./features/relay/index\";\nexport type {\n  RelayClient,\n  RelayClientOptions,\n  StatusTrackerProps,\n  RelayDepositProps,\n  RelayHistoryProps,\n  RelayRecoveryProps,\n  RelayDepositTriggerProps,\n  RelayDepositContentProps,\n  RelayHistoryTriggerProps,\n  RelayHistoryContentProps,\n  RelayRecoveryTriggerProps,\n  RelayRecoveryContentProps,\n  UseRelayConfigOptions,\n  UseRelayConfigResult,\n  UseRelayOrdersOptions,\n  UseRelayOrdersResult,\n  UseRelayRecoveryOptions,\n  UseRelayRecoveryResult,\n  RelayRecoveryQueryKey,\n  RelayRecoveryQueryOptions,\n  RelayRecoveryAction,\n  RelayRecoveryState,\n  RelayRecoveryStep,\n  RelayWalletProps,\n  RecoverySuccessResult,\n  RelayTheme,\n  RelayDrawerDirection,\n  // `SendTransactionFn` already exported from wallet-portfolio above --\n  // re-exported under a relay-specific alias to avoid a duplicate-identifier\n  // clash at this barrel.\n  SendTransactionFn as RelaySendTransactionFn,\n  WriteContractFn,\n  ReadContractFn,\n  SwitchChainFn,\n  WaitForReceiptFn,\n  GetBalanceFn,\n  RelayBalance,\n  RelayTxRequest,\n  RelayContractCall,\n  // Bare `Hex` collides with viem/ethers' identically-named type in consumer\n  // code -- re-exported under a relay-specific alias here, same precedent as\n  // `RelaySendTransactionFn` above. The feature barrel itself still exports\n  // the unaliased `Hex` (no clash inside that namespace).\n  Hex as RelayHex,\n  Catalog,\n  OriginOption,\n  DepositAddressResult,\n  QuoteResult,\n  OrderSummary,\n  RecoveryInfo,\n} from \"./features/relay/index\";\n","import type { Environment } from \"../types\";\n\ntype ProcessWithNextPublicEnv = {\n  env?: {\n    NEXT_PUBLIC_CROSSX_ENVIRONMENT?: string;\n    CROSSX_ENVIRONMENT?: string;\n  };\n};\n\ndeclare const process: ProcessWithNextPublicEnv | undefined;\n\ntype MaybeViteEnv = {\n  env?: Record<string, string | undefined>;\n};\n\nfunction readViteEnv(key: string): string | undefined {\n  try {\n    return (import.meta as unknown as MaybeViteEnv).env?.[key];\n  } catch {\n    return undefined;\n  }\n}\n\nfunction readNodeEnv(key: string): string | undefined {\n  if (typeof process === \"undefined\") return undefined;\n  return process.env?.[key as keyof NonNullable<ProcessWithNextPublicEnv[\"env\"]>];\n}\n\nexport function resolveEnvironment(\n  env?: Environment | \"staging\" | \"prd\" | \"prod\" | \"stg\",\n): Environment {\n  if (env) return normalizeEnvironment(env);\n\n  const crossxEnv =\n    readViteEnv(\"VITE_CROSSX_ENVIRONMENT\") ??\n    readViteEnv(\"NEXT_PUBLIC_CROSSX_ENVIRONMENT\") ??\n    readNodeEnv(\"NEXT_PUBLIC_CROSSX_ENVIRONMENT\") ??\n    readNodeEnv(\"CROSSX_ENVIRONMENT\");\n\n  return normalizeEnvironment(crossxEnv);\n}\n\nfunction normalizeEnvironment(value: string | undefined): Environment {\n  switch (value?.toLowerCase()) {\n    case \"dev\":\n    case \"development\":\n      return \"dev\";\n    case \"stage\":\n    case \"staging\":\n    case \"stg\":\n      return \"stage\";\n    case \"production\":\n    case \"prod\":\n    case \"prd\":\n    default:\n      return \"production\";\n  }\n}\n","import * as React from \"react\";\nimport type { Environment } from \"../types\";\n\n/**\n * S3(CDN)에 올라간 관심사별 설정 JSON(endpoints.json / apps.json 등)을\n * 가져오는 공용 레이어. CDN JSON이 단일 소스다 — 하드코딩 폴백 없음.\n *\n * - 성공 응답은 파일당 세션 내 1회만 fetch(메모이즈). 실패는 캐시하지\n *   않으므로 다음 호출(react-query retry 등)에서 재시도된다.\n * - fetch 실패 시 `getApiBase`는 throw, `useRemoteConfigValue`는 undefined —\n *   호출부는 로딩/에러 상태로 처리한다.\n * - env별 값은 app-launcher-v2.json 컨벤션을 따른다:\n *   값이 env별로 다르면 `{ dev, stage, production }` 객체, 동일하면 문자열.\n */\n\n/** 설정 JSON이 올라가는 CDN 베이스. dev/stage는 staging CDN을 공유한다. */\nconst REMOTE_CONFIG_BASES: Record<Environment, string> = {\n  dev: \"https://stg-contents.crosstoken.io/frontend/common/app-launcher\",\n  stage: \"https://stg-contents.crosstoken.io/frontend/common/app-launcher\",\n  production: \"https://contents.crosstoken.io/frontend/common/app-launcher\",\n};\n\nconst FETCH_TIMEOUT_MS = 3_000;\n\n/** env별로 다르면 객체, 모든 env 동일하면 T 단일 값. */\nexport type RemoteEnvValue<T> = T | Record<Environment, T>;\n\nexport function resolveEnvValue<T>(\n  value: RemoteEnvValue<T> | null | undefined,\n  env: Environment,\n): T | undefined {\n  if (value == null) return undefined;\n  if (\n    typeof value === \"object\" &&\n    !Array.isArray(value) &&\n    (\"dev\" in value || \"stage\" in value || \"production\" in value)\n  ) {\n    return (value as Record<Environment, T>)[env];\n  }\n  return value as T;\n}\n\nconst configCache = new Map<string, Promise<unknown>>();\n\n/**\n * `<CDN base>/<file>`을 fetch해 JSON으로 반환. 실패 시 null.\n * 성공 결과만 URL 단위로 메모이즈한다 — 실패는 캐시하지 않으므로\n * 다음 호출에서 재시도된다.\n */\nexport function fetchRemoteConfig<T>(\n  file: string,\n  env: Environment,\n): Promise<T | null> {\n  const url = `${REMOTE_CONFIG_BASES[env]}/${file}`;\n  let cached = configCache.get(url);\n  if (!cached) {\n    cached = (async () => {\n      const controller = new AbortController();\n      const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n      try {\n        const res = await fetch(url, {\n          cache: \"no-store\",\n          signal: controller.signal,\n        });\n        if (!res.ok) return null;\n        return (await res.json()) as unknown;\n      } catch {\n        return null;\n      } finally {\n        clearTimeout(timer);\n      }\n    })().then((result) => {\n      if (result === null) configCache.delete(url);\n      return result;\n    });\n    configCache.set(url, cached);\n  }\n  return cached as Promise<T | null>;\n}\n\n/** 테스트 전용 — fetch 메모이즈 캐시 초기화. */\nexport function __clearRemoteConfigCacheForTest(): void {\n  configCache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// endpoints.json — 백엔드 API base URL\n// ---------------------------------------------------------------------------\n\nexport type RemoteApiName =\n  | \"walletServer\"\n  | \"bondingCurve\"\n  | \"posa\"\n  | \"gameReward\"\n  | \"gameSwap\";\n\ntype EndpointsConfig = {\n  apis?: Partial<Record<RemoteApiName, RemoteEnvValue<string>>>;\n};\n\nfunction isSafeBaseUrl(value: unknown): value is string {\n  return typeof value === \"string\" && value.startsWith(\"https://\");\n}\n\n/**\n * endpoints.json의 API base를 반환한다. CDN 값이 단일 소스 — fetch 실패\n * 또는 키 누락/비https 값이면 throw하고, react-query retry로 재시도된다.\n * 쿼리 훅의 queryFn 안에서 `await getApiBase(\"posa\", env)` 형태로 사용한다.\n */\nexport async function getApiBase(\n  name: RemoteApiName,\n  env: Environment,\n): Promise<string> {\n  const config = await fetchRemoteConfig<EndpointsConfig>(\n    \"endpoints.json\",\n    env,\n  );\n  const remote = resolveEnvValue(config?.apis?.[name], env);\n  if (!isSafeBaseUrl(remote)) {\n    throw new Error(\n      `[remote-config] endpoints.json missing or invalid api base: ${name} (${env})`,\n    );\n  }\n  return remote;\n}\n\n// ---------------------------------------------------------------------------\n// apps.json — 앱/서비스 링크 (app-launcher JSON에 없는 것만)\n// ---------------------------------------------------------------------------\n\nexport type AppsConfig = {\n  skills?: RemoteEnvValue<string>;\n  gametokenBridge?: RemoteEnvValue<string>;\n  /** 브릿지 버튼을 새창 대신 같은 창으로 여는 gametoken 사이트 URL prefix 목록. */\n  gametokenSites?: string[];\n  bridgePoweredBy?: { url?: RemoteEnvValue<string> };\n  /**\n   * 약관 문서 링크. 브릿지 약관 동의 모달의 Terms of Service 링크가 읽는다 —\n   * cross-game-swap의 legal.json과 같은 운영 방식(무배포 교체). 호출부가\n   * `termsUrl` prop을 넘기면 그 값이 우선, 값이 없으면 DEFAULT_TERMS_URL 폴백.\n   */\n  legal?: { termsUrl?: RemoteEnvValue<string> };\n  /**\n   * Get ONEUSD chooser action visibility. Swap/Bridge default to enabled;\n   * Transfer Crypto defaults to disabled when remote config is unavailable.\n   */\n  getOneUsd?: {\n    modes?: {\n      swap?: boolean;\n      bridge?: boolean;\n      transferCrypto?: boolean;\n    };\n    /**\n     * 액션별 최소 dapp-ui 버전. 호스트가 설치한 dapp-ui 가 이 값보다 낮으면\n     * `modes`가 true여도 해당 액션을 숨긴다 — 구버전에서 깨지는 기능을 호스트\n     * 재배포 없이 끄기 위한 장치. 값이 없으면 버전 제한 없음, 형식이 깨져\n     * 있으면 통과(설정 오타로 전체가 사라지지 않게).\n     */\n    minVersions?: {\n      swap?: string;\n      bridge?: string;\n      transferCrypto?: string;\n    };\n  };\n  /**\n   * 토큰 상세화면 섹션별 노출 여부. 값이 없으면 기본값(history·balance만 노출).\n   * 섹션 키: chart / balance(=My Balance + Send) / overview / social / history.\n   * env별 값은 apps.json 파일 자체가 환경마다 분리돼 있으므로 단순 boolean만 받는다.\n   */\n  tokenDetail?: {\n    sections?: {\n      chart?: boolean;\n      balance?: boolean;\n      overview?: boolean;\n      social?: boolean;\n      history?: boolean;\n    };\n  };\n  /**\n   * Relay(외부체인 → CROSS 입금) 운영 설정.\n   * `minDepositAmount`: 입금 위저드 Continue를 막는 최소 수량(사람 단위,\n   * 토큰 공통). CDN 값이 없거나 fetch 실패 시 호출부 폴백(6)이 적용된다 —\n   * endpoints.json과 달리 UI 게이트라 폴백을 허용한다.\n   */\n  relay?: {\n    minDepositAmount?: RemoteEnvValue<number | string>;\n    /**\n     * `maxDepositAmount`: Continue를 막는 최대 수량(사람 단위, 토큰 공통).\n     * 브릿지/DEX 유동성 한도를 운영이 수동 반영하는 값 — 미설정 시 상한 없음.\n     */\n    maxDepositAmount?: RemoteEnvValue<number | string>;\n    /**\n     * Role-labelled Relay factory addresses. New SDKs prefer this field so\n     * operators can tell the StandingForwarderFactory and\n     * DepositorForwarderFactory apart without relying on array order.\n     *\n     * A legacy `trustedFactories` field, if encountered, is deliberately not\n     * typed or read by current code.\n     */\n    forwarderFactories?: RemoteEnvValue<{\n      standingForwarderFactory?: string;\n      depositorForwarderFactory?: string;\n    }>;\n  };\n};\n\n/**\n * Resolves Relay's role-labelled factory config to the allowlist shape consumed\n * by the verification layer. A legacy `trustedFactories` field is\n * intentionally ignored by current code.\n */\nexport function resolveRelayForwarderFactories(\n  config: AppsConfig | null | undefined,\n  env: Environment,\n): string[] | undefined {\n  const labelled = resolveEnvValue(config?.relay?.forwarderFactories, env);\n  const labelledAddresses = [\n    labelled?.standingForwarderFactory,\n    labelled?.depositorForwarderFactory,\n  ].filter((value): value is string => typeof value === \"string\");\n\n  return labelledAddresses.length > 0 ? labelledAddresses : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// chains.json — 체인 표시 메타데이터 (표시명·네이티브 심볼)\n//\n// 체인명·네이티브 심볼은 온체인에서 조회할 수 없는 오프체인 메타데이터다.\n// RPC로 얻는 건 chainId 뿐이고, 네이티브 가스 토큰은 컨트랙트가 없어\n// symbol()/name()도 없다. 따라서 CDN JSON을 표시용 단일 소스로 둔다.\n//\n// chainId(10진 문자열)를 키로 매핑한다. 순수 표시(DISPLAY) 전용 —\n// 온체인 티커·외부 API 코드(AlchemyPay onramp, 토큰리스트 심볼 비교 등)로\n// 재사용하지 말 것. (예: 네이티브 심볼 'ONE'을 잔액매칭 로직에 쓰면 깨진다.)\n//\n// 예시 chains.json:\n//   {\n//     \"chains\": {\n//       \"612055\": { \"name\": \"One Mainnet\", \"nativeSymbol\": \"ONE\" },\n//       \"612044\": { \"name\": \"One Testnet\", \"nativeSymbol\": \"tONE\" }\n//     }\n//   }\n// ---------------------------------------------------------------------------\n\nexport const CHAINS_CONFIG_FILE = \"chains.json\";\n\n/** 체인 하나의 표시 메타. 지정 안 된 필드는 호출부 폴백(온체인/하드코딩)으로. */\nexport type ChainDisplayMeta = {\n  /** 체인 표시명 (예: 'One Mainnet'). */\n  name?: string;\n  /** 네이티브 통화 표시 심볼 (예: 'ONE' / 'tONE'). DISPLAY 전용. */\n  nativeSymbol?: string;\n};\n\n/** chains.json 스키마 — chainId(10진 문자열) → 표시 메타. */\nexport type ChainsConfig = {\n  chains?: Record<string, ChainDisplayMeta>;\n};\n\n/**\n * chains.json에서 특정 chainId의 표시 메타를 비동기로 반환한다.\n * fetch 실패·미정의면 `{}` — 호출부에서 `?? 폴백`으로 처리.\n * React 밖(설정 생성·유틸)에서 사용.\n */\nexport async function getChainDisplay(\n  chainId: number,\n  env: Environment,\n): Promise<ChainDisplayMeta> {\n  const config = await fetchRemoteConfig<ChainsConfig>(CHAINS_CONFIG_FILE, env);\n  return config?.chains?.[String(chainId)] ?? {};\n}\n\n/**\n * 컴포넌트에서 chainId의 표시명·네이티브 심볼을 구독하는 훅.\n * chainId 변경(네트워크 전환) 시 재조회한다. 로딩 중/미정의면 각 필드\n * undefined — 호출부에서 `?? 온체인/하드코딩 폴백`으로 처리한다.\n */\nexport function useChainDisplay(\n  chainId: number | undefined,\n  env: Environment,\n): ChainDisplayMeta {\n  const [meta, setMeta] = React.useState<ChainDisplayMeta>({});\n\n  React.useEffect(() => {\n    if (chainId == null) {\n      setMeta({});\n      return;\n    }\n    let alive = true;\n    fetchRemoteConfig<ChainsConfig>(CHAINS_CONFIG_FILE, env).then((config) => {\n      if (!alive) return;\n      setMeta(config?.chains?.[String(chainId)] ?? {});\n    });\n    return () => {\n      alive = false;\n    };\n  }, [chainId, env]);\n\n  return meta;\n}\n\n/**\n * chains.json의 전체 chainId→표시 메타 맵을 구독한다. 여러 체인의 표시명을\n * 한 번에 매핑해야 하는 곳(네트워크 스위처 등)에서 사용한다. 로딩 전/실패 시\n * 빈 객체 — 호출부에서 `map[String(id)]?.name ?? 폴백`으로 처리한다.\n */\nexport function useChainsConfig(\n  env: Environment,\n): Record<string, ChainDisplayMeta> {\n  const [chains, setChains] = React.useState<Record<string, ChainDisplayMeta>>(\n    {},\n  );\n\n  React.useEffect(() => {\n    let alive = true;\n    fetchRemoteConfig<ChainsConfig>(CHAINS_CONFIG_FILE, env).then((config) => {\n      if (!alive) return;\n      setChains(config?.chains ?? {});\n    });\n    return () => {\n      alive = false;\n    };\n  }, [env]);\n\n  return chains;\n}\n\n/**\n * 컴포넌트에서 원격 설정 값 하나를 구독하는 최소 훅.\n * react-query 의존 없이 동작한다 (QueryClientProvider 없는 소비자도 안전).\n * 로딩 중/실패 시 undefined — 호출부에서 `?? 폴백`으로 처리.\n */\nexport function useRemoteConfigValue<TConfig, TValue>(\n  file: string,\n  env: Environment,\n  select: (config: TConfig | null) => TValue | undefined,\n): TValue | undefined {\n  const [value, setValue] = React.useState<TValue | undefined>(undefined);\n  const selectRef = React.useRef(select);\n  selectRef.current = select;\n\n  React.useEffect(() => {\n    let alive = true;\n    fetchRemoteConfig<TConfig>(file, env).then((config) => {\n      if (!alive) return;\n      const next = selectRef.current(config);\n      if (next !== undefined) setValue(next);\n    });\n    return () => {\n      alive = false;\n    };\n  }, [file, env]);\n\n  return value;\n}\n","export const DAPP_UI_VERSION = '1.4.1';\n","import { DAPP_UI_VERSION } from \"./version\";\n\nexport type AppLauncherUsageMode = \"dapp-ui\" | \"connect-kit-react\";\n\ninterface RuntimeLogState {\n  mode: AppLauncherUsageMode;\n  connectKitVersion?: string;\n  scheduled?: boolean;\n  loggedMode?: AppLauncherUsageMode;\n}\n\nconst GLOBAL_KEY = \"__nexusCrossAppLauncherRuntime\";\n\nfunction getState(): RuntimeLogState | null {\n  if (typeof window === \"undefined\") return null;\n  const global = window as typeof window & {\n    [GLOBAL_KEY]?: RuntimeLogState;\n  };\n  global[GLOBAL_KEY] ??= { mode: \"dapp-ui\" };\n  return global[GLOBAL_KEY];\n}\n\nfunction scheduleFlush(state: RuntimeLogState): void {\n  if (state.scheduled) return;\n  state.scheduled = true;\n\n  const flush = () => {\n    state.scheduled = false;\n    if (state.loggedMode === state.mode) return;\n\n    if (state.mode === \"connect-kit-react\") {\n      const kitVersion = state.connectKitVersion ?? \"unknown\";\n      console.info(\n        `[CROSSx App Launcher] connect-kit-react v${kitVersion} 초기화 완료 ` +\n          `(dapp-ui v${DAPP_UI_VERSION}, 사용 모드: connect-kit)`,\n      );\n    } else {\n      console.info(\n        `[CROSSx App Launcher] dapp-ui v${DAPP_UI_VERSION} 초기화 완료 ` +\n          `(사용 모드: dapp-ui only)`,\n      );\n    }\n\n    state.loggedMode = state.mode;\n  };\n\n  if (typeof queueMicrotask === \"function\") {\n    queueMicrotask(flush);\n  } else {\n    Promise.resolve().then(flush);\n  }\n}\n\nexport function announceAppLauncherUsage(options?: {\n  mode?: AppLauncherUsageMode;\n  connectKitVersion?: string;\n}): void {\n  const state = getState();\n  if (!state) return;\n\n  if (options?.mode === \"connect-kit-react\") {\n    state.mode = \"connect-kit-react\";\n  } else if (!state.mode) {\n    state.mode = \"dapp-ui\";\n  }\n\n  if (options?.connectKitVersion) {\n    state.connectKitVersion = options.connectKitVersion;\n  }\n\n  scheduleFlush(state);\n}\n","import {\n  BrowserClient,\n  Scope,\n  defaultStackParser,\n  linkedErrorsIntegration,\n  makeFetchTransport,\n} from \"@sentry/react\";\n\nimport type { Environment } from \"../../types\";\nimport { resolveEnvironment } from \"../environment\";\nimport { DAPP_UI_VERSION } from \"../version\";\n\n// DSN is a public value; the auth token for source-map upload must never live in code.\nconst DAPP_UI_SENTRY_DSN =\n  \"https://5c82aeb32da33b5a0a14b7c5393992ca@o4511668907081728.ingest.de.sentry.io/4511705324126289\";\n\n// Sentry rejects slashes in release names, so the npm package name can't be used.\n// Must stay in sync with the sentry-cli upload release in tsup.config.ts.\nconst DAPP_UI_SENTRY_RELEASE = `dapp-ui@${DAPP_UI_VERSION}`;\n\ntype SentryEnvironment = \"dev\" | \"stg\" | \"prod\";\n\nexport interface InitDappUiSentryOptions {\n  /** Falls back to VITE_CROSSX_ENVIRONMENT / NEXT_PUBLIC_CROSSX_ENVIRONMENT detection. */\n  environment?: Environment | \"dev\" | \"stg\" | \"staging\" | \"prod\" | \"prd\";\n  /** Defaults to true only on prod; dev/stg init but do not send. */\n  enabled?: boolean;\n  /** Override DSN (tests / self-hosted relay). */\n  dsn?: string;\n  /** 0–1 sample rate for ui:/funnel: analytics events (default 0.1). Errors are never sampled. */\n  analyticsSampleRate?: number;\n}\n\nlet dappUiScope: Scope | null = null;\nlet analyticsSampleRate = 0.1;\n\nexport function getAnalyticsSampleRate(): number {\n  return analyticsSampleRate;\n}\n\nfunction toSentryEnvironment(environment: Environment): SentryEnvironment {\n  switch (environment) {\n    case \"dev\":\n      return \"dev\";\n    case \"stage\":\n      return \"stg\";\n    default:\n      return \"prod\";\n  }\n}\n\n/**\n * dapp-ui is embedded in host apps that may run their own Sentry, so we never\n * call `Sentry.init()`: it claims the global hub and either clobbers the host\n * client or gets clobbered by it. Instead we keep a dedicated BrowserClient on\n * an isolated Scope, with no global integrations (no window.onerror, no\n * fetch/XHR patching) — errors reach the dapp-ui project only through explicit\n * captureDappUiException calls, and the host's Sentry is never touched.\n */\nexport function initDappUiSentry(options?: InitDappUiSentryOptions): Scope | null {\n  if (dappUiScope) return dappUiScope;\n  if (typeof window === \"undefined\") return null;\n\n  const environment = toSentryEnvironment(resolveEnvironment(options?.environment));\n\n  if (typeof options?.analyticsSampleRate === \"number\") {\n    analyticsSampleRate = Math.min(1, Math.max(0, options.analyticsSampleRate));\n  }\n\n  // No dedupeIntegration: it drops consecutive identical events, which would\n  // swallow repeated ui:/funnel: analytics messages (same button clicked twice).\n  const client = new BrowserClient({\n    dsn: options?.dsn ?? DAPP_UI_SENTRY_DSN,\n    transport: makeFetchTransport,\n    stackParser: defaultStackParser,\n    integrations: [linkedErrorsIntegration()],\n    environment,\n    release: DAPP_UI_SENTRY_RELEASE,\n    enabled: options?.enabled ?? environment === \"prod\",\n  });\n\n  const scope = new Scope();\n  scope.setClient(client);\n  scope.setTags({ package: \"dapp-ui\", framework: \"react\", source: \"dapp-ui\" });\n  client.init();\n\n  dappUiScope = scope;\n  return dappUiScope;\n}\n\nexport function getDappUiSentryScope(): Scope | null {\n  return dappUiScope;\n}\n\n/** Lazily initializes with defaults so error boundaries work without host setup. */\nexport function captureDappUiException(\n  error: unknown,\n  extra?: Record<string, unknown>,\n): string | undefined {\n  const scope = dappUiScope ?? initDappUiSentry();\n  if (!scope) return undefined;\n  return scope.captureException(error, extra ? { captureContext: { extra } } : undefined);\n}\n","import { Component, type ErrorInfo, type ReactNode } from \"react\";\n\nimport { captureDappUiException } from \"./client\";\n\nexport interface DappUiErrorBoundaryProps {\n  children: ReactNode;\n  /** Rendered when a child throws; defaults to rendering nothing. */\n  fallback?: ReactNode;\n  /** Identifies which popup/root failed, e.g. \"app-launcher\". */\n  name?: string;\n}\n\ninterface DappUiErrorBoundaryState {\n  hasError: boolean;\n}\n\n/**\n * Reports to the isolated dapp-ui Sentry client instead of the global hub —\n * Sentry.ErrorBoundary would send to whichever client the host app installed.\n */\nexport class DappUiErrorBoundary extends Component<\n  DappUiErrorBoundaryProps,\n  DappUiErrorBoundaryState\n> {\n  state: DappUiErrorBoundaryState = { hasError: false };\n\n  static getDerivedStateFromError(): DappUiErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  componentDidCatch(error: Error, info: ErrorInfo): void {\n    captureDappUiException(error, {\n      componentStack: info.componentStack ?? undefined,\n      boundary: this.props.name ?? \"dapp-ui\",\n    });\n  }\n\n  render(): ReactNode {\n    if (this.state.hasError) return this.props.fallback ?? null;\n    return this.props.children;\n  }\n}\n","export function cn(...classes: (string | false | null | undefined)[]): string {\n  return classes.filter(Boolean).join(\" \");\n}\n\n/**\n * 지갑 인앱 브라우저 판별 (UA 기반).\n *\n * 역대 브랜드 표기를 전부 본다(CROSSx → ONEwallet → ONEpocket). 구버전 앱 빌드가\n * 필드에 오래 남으므로 최신 표기 하나만 보면 그 빌드가 통째로 미검출된다.\n * 표기 사이 공백(`One Wallet`)과 빌드 스킴 접미사(`+`, `:stage`)도 허용한다.\n */\nconst WALLET_UA_PATTERN = /CROSSx|ONE\\s?wallet|ONE\\s?pocket/i;\n\nexport function isInAppBrowser(): boolean {\n  if (typeof window === \"undefined\") return false;\n  return WALLET_UA_PATTERN.test(window.navigator.userAgent);\n}\n","// Anonymous analytics identity. Raw wallet addresses must never leave the\n// browser (PII rule) — only a truncated SHA-256 digest is used.\n\nconst STORAGE_KEY = \"__nexus_dapp_ui_uid\";\n\nlet memoryAnonId: string | null = null;\n\nfunction randomUuid(): string {\n  if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n    return crypto.randomUUID();\n  }\n  return Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join(\"\");\n}\n\n/** Stable per-browser id, e.g. `anon_3f8a…`. Falls back to memory when localStorage throws. */\nexport function getDappUiAnonId(): string {\n  if (memoryAnonId) return memoryAnonId;\n  let stored: string | null = null;\n  try {\n    stored = window.localStorage.getItem(STORAGE_KEY);\n  } catch {\n    // private mode / storage disabled\n  }\n  if (!stored || !stored.startsWith(\"anon_\")) {\n    stored = `anon_${randomUuid()}`;\n    try {\n      window.localStorage.setItem(STORAGE_KEY, stored);\n    } catch {\n      // keep in memory only\n    }\n  }\n  memoryAnonId = stored;\n  return stored;\n}\n\n/** `wallet_<first 16 hex chars of sha256(lowercased address)>`; null when hashing is unavailable. */\nexport async function hashWalletAddress(address: string): Promise<string | null> {\n  if (typeof crypto === \"undefined\" || !crypto.subtle) return null;\n  try {\n    const bytes = new TextEncoder().encode(address.trim().toLowerCase());\n    const digest = await crypto.subtle.digest(\"SHA-256\", bytes);\n    const hex = Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n    return `wallet_${hex.slice(0, 16)}`;\n  } catch {\n    return null;\n  }\n}\n","import type { Scope } from \"@sentry/react\";\n\nimport { isInAppBrowser } from \"../utils\";\nimport { getAnalyticsSampleRate, getDappUiSentryScope, initDappUiSentry } from \"./client\";\nimport { getDappUiAnonId, hashWalletAddress } from \"./user-id\";\n\nexport type DappUiFeature =\n  | \"app_launcher\"\n  | \"bridge\"\n  | \"connect_button\"\n  | \"get_one_usd\"\n  | \"relay\"\n  | \"send\"\n  | \"skills\"\n  | \"wallet_connect\"\n  | \"wallet_info\"\n  | \"wallet_portfolio\";\n\nexport type DappUiFlow = \"connect\" | \"send\" | \"bridge\" | \"withdraw\" | \"relay\";\n\nexport type DappUiFailureReason =\n  | \"user-rejected\"\n  | \"insufficient-gas\"\n  | \"timeout\"\n  | \"contract-reverted\"\n  | \"network\"\n  | \"unknown\";\n\nexport interface TrackDappUiFunnelOptions {\n  status?: \"success\" | \"failure\";\n  reason?: DappUiFailureReason;\n  tags?: Record<string, string>;\n}\n\ntype DeviceType = \"desktop\" | \"mobile\" | \"tablet\";\n\nfunction detectDeviceType(): DeviceType {\n  const ua = window.navigator.userAgent;\n  if (/ipad|tablet|(android(?!.*mobile))/i.test(ua)) return \"tablet\";\n  if (/mobile|iphone|ipod|android/i.test(ua)) return \"mobile\";\n  return \"desktop\";\n}\n\nfunction commonTags(): Record<string, string> {\n  return {\n    host: window.location.hostname,\n    device_type: detectDeviceType(),\n    is_cross_inapp: String(isInAppBrowser()),\n  };\n}\n\nlet userInitialized = false;\n\n/** Lazy-inits the isolated client and stamps the anon user id on first use. */\nfunction ensureAnalyticsScope(): Scope | null {\n  if (typeof window === \"undefined\") return null;\n  const scope = getDappUiSentryScope() ?? initDappUiSentry();\n  if (!scope) return null;\n  if (!userInitialized) {\n    userInitialized = true;\n    scope.setUser({ id: getDappUiAnonId() });\n  }\n  return scope;\n}\n\nfunction sampledOut(): boolean {\n  return Math.random() >= getAnalyticsSampleRate();\n}\n\n/**\n * `wallet_*` while an address is connected, `anon_*` otherwise. Called\n * automatically by WalletInfo/ConnectButton from their address props; hosts\n * only need it for custom setups. Only a truncated hash of the address is sent.\n */\nlet userUpdateSeq = 0;\n\nexport function setDappUiAnalyticsUser(address?: string): void {\n  const scope = ensureAnalyticsScope();\n  if (!scope) return;\n  const seq = ++userUpdateSeq;\n  if (!address) {\n    scope.setUser({ id: getDappUiAnonId() });\n    return;\n  }\n  void hashWalletAddress(address).then((walletId) => {\n    // A newer call (e.g. disconnect) may have landed while hashing — don't clobber it.\n    if (walletId && seq === userUpdateSeq) scope.setUser({ id: walletId });\n  });\n}\n\n/** Button/UI usage event: message `ui: <feature>_<action>` on the isolated client. */\nexport function trackDappUiEvent(\n  feature: DappUiFeature,\n  action: string,\n  tags?: Record<string, string>,\n): void {\n  const scope = ensureAnalyticsScope();\n  if (!scope || sampledOut()) return;\n  scope.captureMessage(`ui: ${feature}_${action}`, \"info\", {\n    captureContext: { tags: { ...commonTags(), feature, action, ...tags } },\n  });\n}\n\n/** Funnel step event: message `funnel: <flow>_<step>` with funnel_status/failure_reason tags. */\nexport function trackDappUiFunnel(\n  flow: DappUiFlow,\n  step: string,\n  options?: TrackDappUiFunnelOptions,\n): void {\n  const scope = ensureAnalyticsScope();\n  if (!scope || sampledOut()) return;\n  const status = options?.status ?? \"success\";\n  scope.captureMessage(`funnel: ${flow}_${step}`, \"info\", {\n    captureContext: {\n      tags: {\n        ...commonTags(),\n        flow,\n        funnel_step: `${flow}_${step}`,\n        funnel_status: status,\n        ...(status === \"failure\" ? { failure_reason: options?.reason ?? \"unknown\" } : {}),\n        ...options?.tags,\n      },\n    },\n  });\n}\n\n/** Maps arbitrary wallet/RPC errors onto the fixed failure_reason vocabulary. */\nexport function normalizeFailureReason(error: unknown): DappUiFailureReason {\n  const err = error as { code?: unknown; message?: unknown } | null | undefined;\n  const message = String(\n    (err && typeof err === \"object\" && \"message\" in err ? err.message : error) ?? \"\",\n  ).toLowerCase();\n\n  if (err?.code === 4001 || /reject|denied|declin|cancel/i.test(message)) return \"user-rejected\";\n  if (/insufficient (funds|gas|balance)|exceeds balance/i.test(message)) return \"insufficient-gas\";\n  if (/timeout|timed out|deadline/i.test(message)) return \"timeout\";\n  if (/revert|execution failed/i.test(message)) return \"contract-reverted\";\n  if (/network|fetch|connection|disconnected|offline/i.test(message)) return \"network\";\n  return \"unknown\";\n}\n","import { useCallback, type MouseEvent, type ReactNode } from \"react\";\n\nimport { trackDappUiEvent, type DappUiFeature } from \"./analytics\";\n\nexport interface TrackingBoundaryProps {\n  feature: DappUiFeature;\n  children: ReactNode;\n}\n\ntype TrackedNativeEvent = Event & { __dappUiTracked?: boolean };\n\n/**\n * Click-delegation root embedded in each feature. Any descendant with a\n * `data-track=\"<action>\"` attribute (optionally `data-track-id` for dynamic\n * list items) is reported as `ui: <feature>_<action>` — no per-button handler\n * code. Uses the bubble phase so that when boundaries nest (SendFlow inside\n * WalletInfo), the innermost feature claims the event first and the outer\n * boundary skips it via the native-event flag. Portal clicks still arrive\n * because React propagates synthetic events through the component tree.\n */\nexport function TrackingBoundary({ feature, children }: TrackingBoundaryProps) {\n  const handleClick = useCallback(\n    (event: MouseEvent<HTMLDivElement>) => {\n      const nativeEvent = event.nativeEvent as TrackedNativeEvent;\n      if (nativeEvent.__dappUiTracked) return;\n\n      const target = event.target as Element | null;\n      const tracked = target?.closest?.(\"[data-track]\");\n      if (!tracked) return;\n      const action = tracked.getAttribute(\"data-track\");\n      if (!action) return;\n\n      nativeEvent.__dappUiTracked = true;\n      const itemId = tracked.getAttribute(\"data-track-id\");\n      trackDappUiEvent(feature, action, itemId ? { item_id: itemId } : undefined);\n    },\n    [feature],\n  );\n\n  return (\n    <div style={{ display: \"contents\" }} onClick={handleClick}>\n      {children}\n    </div>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { Drawer } from \"vaul\";\nimport { AppLauncherContext } from \"../providers/app-launcher-provider\";\nimport { useHistoryDismiss } from \"../../../hooks/use-history-dismiss\";\nimport { useMediaQuery } from \"../../../hooks/use-media-query\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport type { Environment, Theme } from \"../../../types\";\nimport \"../styles/theme.css\";\nimport \"../styles/base.css\";\nimport \"../styles/keyframes.css\";\nimport \"../styles/component.css\";\nimport \"../styles/utilities.css\";\n\nexport interface AppLauncherProps {\n  env?: Environment;\n  theme?: Theme;\n  mobileBreakpoint?: number;\n  domain?: string;\n  children: React.ReactNode;\n}\n\nexport function AppLauncher({\n  env,\n  theme = \"dark\",\n  mobileBreakpoint = 768,\n  domain,\n  children,\n}: AppLauncherProps) {\n  const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint}px)`);\n  const [open, setOpen] = React.useState(false);\n  const close = React.useCallback(() => setOpen(false), []);\n  const resolvedEnv = resolveEnvironment(env);\n\n  // Android 백버튼 / iOS 백스와이프로 닫히게 한다.\n  useHistoryDismiss(open, close);\n\n  const contextValue = React.useMemo(\n    () => ({ env: resolvedEnv, theme, isMobile, open, domain, close }),\n    [resolvedEnv, theme, isMobile, open, domain, close],\n  );\n\n  return (\n    <AppLauncherContext.Provider value={contextValue}>\n      {isMobile ? (\n        <Drawer.Root\n          key=\"drawer\"\n          direction=\"right\"\n          open={open}\n          onOpenChange={setOpen}\n          noBodyStyles\n        >\n          {children}\n        </Drawer.Root>\n      ) : (\n        <Popover.Root key=\"popover\" open={open} onOpenChange={setOpen}>\n          {children}\n        </Popover.Root>\n      )}\n    </AppLauncherContext.Provider>\n  );\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { Environment, Theme } from \"../../../types\";\n\nexport interface AppLauncherContextValue {\n  env: Environment;\n  theme: Theme;\n  isMobile: boolean;\n  open: boolean;\n  domain?: string;\n  close: () => void;\n}\n\nexport const AppLauncherContext = createContext<AppLauncherContextValue | null>(\n  null,\n);\n\nexport function useAppLauncherContext() {\n  const ctx = useContext(AppLauncherContext);\n  if (!ctx) {\n    throw new Error(\n      \"AppLauncher compound components must be used within <AppLauncher>\",\n    );\n  }\n  return ctx;\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nconst MARKER = \"__dappUiOverlay\";\n\ntype Entry = { close: () => void };\n\n/**\n * 열려 있는 오버레이 스택(LIFO). 모듈 단위로 공유한다 — 백 제스처는 항상\n * \"가장 위\" 오버레이 하나만 닫아야 하는데, 훅 인스턴스는 서로를 모른다.\n */\nconst stack: Entry[] = [];\n\n/** 우리가 밀어 넣은 history 엔트리 수. 항상 `stack.length`로 수렴시킨다. */\nlet ourEntries = 0;\n\n/** 우리가 스스로 유발한 뒤로가기 — 이 popstate는 사용자 제스처가 아니다. */\nlet pendingSelfBack = 0;\n\nlet listening = false;\nlet reconcileScheduled = false;\n\nfunction handlePopState() {\n  if (pendingSelfBack > 0) {\n    pendingSelfBack -= 1;\n    return;\n  }\n  ourEntries = Math.max(0, ourEntries - 1);\n  stack.pop()?.close();\n  stopListeningIfIdle();\n}\n\nfunction ensureListening() {\n  if (listening) return;\n  window.addEventListener(\"popstate\", handlePopState);\n  listening = true;\n}\n\nfunction stopListeningIfIdle() {\n  if (stack.length > 0 || !listening) return;\n  window.removeEventListener(\"popstate\", handlePopState);\n  listening = false;\n}\n\n/**\n * 엔트리 수를 스택 깊이에 맞춘다. **같은 tick의 변화가 모두 끝난 뒤** 한 번만\n * 실행되는 것이 핵심 — 오버레이 A를 닫고 곧바로 B를 여는 흐름(Get ONEUSD의\n * 액션 선택 → Swap 화면)에서는 깊이가 그대로 1이므로 A의 엔트리를 B가 그냥\n * 물려받고, 되돌리기(back)가 아예 일어나지 않는다. 닫을 때 즉시 back()을 하면\n * 그 back이 B가 밀어 넣은 엔트리를 때린다.\n */\nfunction scheduleReconcile() {\n  if (reconcileScheduled) return;\n  reconcileScheduled = true;\n  queueMicrotask(() => {\n    reconcileScheduled = false;\n    const surplus = ourEntries - stack.length;\n    if (surplus <= 0) {\n      stopListeningIfIdle();\n      return;\n    }\n    ourEntries -= surplus;\n    pendingSelfBack += 1; // go(-n)은 popstate를 한 번 발생시킨다.\n    window.history.go(-surplus);\n    stopListeningIfIdle();\n  });\n}\n\n/**\n * Lets the platform's own \"go back\" gesture dismiss an overlay: Android's\n * hardware/system back button, iOS Safari's back-swipe, and the browser Back\n * button all emit `popstate`, which none of radix/vaul handle — without this the\n * back button leaves the host page instead of closing the modal on top of it.\n *\n * 열려 있는 동안 history 엔트리 하나를 차지하고 공유 스택에 올라간다. 백 제스처는\n * 최상위 오버레이 하나만 닫고, UI로 닫으면(X·오버레이 클릭·Esc) 그 엔트리를\n * 반납해 히스토리가 원래 자리로 돌아온다.\n */\nexport function useHistoryDismiss(open: boolean, close: () => void): void {\n  const closeRef = React.useRef(close);\n  closeRef.current = close;\n\n  React.useEffect(() => {\n    if (!open || typeof window === \"undefined\") return undefined;\n    return pushOverlay(() => closeRef.current());\n  }, [open]);\n}\n\n/**\n * 훅의 알맹이 — React 없이 동작하므로 그대로 테스트할 수 있다.\n * 등록하고, 해제 함수를 돌려준다.\n */\nexport function pushOverlay(close: () => void): () => void {\n  const entry: Entry = { close };\n  stack.push(entry);\n  if (ourEntries < stack.length) {\n    window.history.pushState({ [MARKER]: true }, \"\");\n    ourEntries += 1;\n  }\n  ensureListening();\n\n  return () => {\n    const index = stack.lastIndexOf(entry);\n    if (index === -1) return; // 이미 백 제스처로 닫힌 엔트리.\n    stack.splice(index, 1);\n    scheduleReconcile();\n  };\n}\n","\"use client\";\n\nimport { useState, useEffect } from \"react\";\n\nexport function useMediaQuery(query: string): boolean {\n  const [matches, setMatches] = useState(false);\n\n  useEffect(() => {\n    const mql = window.matchMedia(query);\n    setMatches(mql.matches);\n\n    const handler = (e: MediaQueryListEvent) => setMatches(e.matches);\n    mql.addEventListener(\"change\", handler);\n    return () => mql.removeEventListener(\"change\", handler);\n  }, [query]);\n\n  return matches;\n}\n","\n          export default function styleInject(css, { insertAt } = {}) {\n            if (!css || typeof document === 'undefined') return\n          \n            const head = document.head || document.getElementsByTagName('head')[0]\n            const style = document.createElement('style')\n            style.type = 'text/css'\n          \n            if (insertAt === 'top') {\n              if (head.firstChild) {\n                head.insertBefore(style, head.firstChild)\n              } else {\n                head.appendChild(style)\n              }\n            } else {\n              head.appendChild(style)\n            }\n          \n            if (style.styleSheet) {\n              style.styleSheet.cssText = css\n            } else {\n              style.appendChild(document.createTextNode(css))\n            }\n          }\n          ","import styleInject from '#style-inject';styleInject(\"[data-theme=dark]{--al-bg: var(--ds-surface-default, #1e232e);--al-bg-hover: var(--ds-surface-medium, #252b39);--al-text: var(--ds-content-highest, #ffffff);--al-text-secondary: var(--ds-content-medium, #ffffffa6);--al-border: var(--ds-border-default, #ffffff12);--al-shadow: var(--ds-shadow-modal, 0px 4px 30px -1px rgba(0, 0, 0, .7));--al-primary: #f3742e;--al-primary-hover: #ff8a4a;--al-primary-soft: #3a261f;--al-new-ribbon-bg: #ed0a72;--al-badge-bg: var(--ds-accent-primary-low, #422a1f);--al-badge-text: var(--ds-accent-primary-default, #f0640d);--al-drawer-overlay: var(--ds-overlay-dim, #000000b3)}[data-theme=light]{--al-bg: var(--ds-surface-default, #ffffff);--al-bg-hover: var(--ds-surface-medium, #f5f7fa);--al-text: var(--ds-content-highest, #000000);--al-text-secondary: var(--ds-content-medium, #000000a6);--al-border: var(--ds-border-default, #00000012);--al-shadow: var(--ds-shadow-modal, 0px 4px 30px -1px rgba(0, 0, 0, .15));--al-primary: #f3742e;--al-primary-hover: #ff8a4a;--al-primary-soft: #fff1eb;--al-new-ribbon-bg: #ed0a72;--al-badge-bg: var(--ds-accent-primary-low, #fff5ee);--al-badge-text: var(--ds-accent-primary-default, #ff8438);--al-drawer-overlay: var(--ds-overlay-dim, #00000080)}\\n\")","import styleInject from '#style-inject';styleInject(\".al-trigger-btn{--at-bg: transparent;--at-bg-hover: #eaedf1;--at-color: var(--ds-content-high, rgba(0, 0, 0, .85));--at-border: none;--at-border-radius: 8px;--at-size: 36px;--at-padding: 0;--at-press-scale: .95;--at-transition: background-color .15s ease;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:var(--at-size);height:var(--at-size);padding:var(--at-padding);border:var(--at-border);background:var(--at-bg);border-radius:var(--at-border-radius);color:var(--at-color);cursor:pointer;transition:var(--at-transition)}.al-trigger-btn:hover,.al-trigger-btn[data-state=open]{background:var(--at-bg-hover)}.al-trigger-btn:active:not(:disabled){background:var(--at-bg-hover);transform:scale(var(--at-press-scale))}.al-trigger-btn[data-theme=dark]{--at-color: var(--ds-content-high, rgba(255, 255, 255, .85));--at-bg-hover: #252b39}.al-trigger-btn[data-theme=light]{--at-color: var(--ds-content-high, rgba(0, 0, 0, .85))}\\n\")","import styleInject from '#style-inject';styleInject(\"@keyframes al-fade-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}\\n\")","import styleInject from '#style-inject';styleInject(\".al-popover-content{-webkit-font-smoothing:antialiased;z-index:100001;border-radius:24px;background:var(--al-bg);border:1px solid var(--al-popover-border, var(--al-border));box-shadow:var(--al-popover-shadow, var(--al-shadow));animation:al-fade-in .15s ease-out;max-height:80vh;overflow-x:hidden;overflow-y:auto;padding:20px 4px;box-sizing:border-box}.al-popover-content .al-menu{display:flex;flex-direction:column;width:424px;max-width:calc(100vw - 32px);padding:0 12px;box-sizing:border-box}.al-popover-content .al-grid{display:grid;grid-template-columns:repeat(4,100px);row-gap:12px;column-gap:0;justify-content:center;align-items:start}.al-popover-content .al-grid-item{position:relative;flex-direction:column;justify-content:flex-start;align-items:center;gap:6px;padding:12px 0;width:100px;min-width:0;border-radius:10px}.al-popover-content .al-grid-item-left{flex-direction:column;align-items:center;gap:8px;width:100%;overflow:visible}.al-popover-content .al-grid-item-icon{width:50px;height:50px;border-radius:10.125px;overflow:visible}.al-popover-content .al-grid-item-icon img{object-fit:cover}.al-popover-content .al-grid-item-label{font-family:Inter,sans-serif;font-size:12px;font-weight:500;line-height:1.1;letter-spacing:0;text-align:center;color:var(--al-text);max-width:100%;white-space:normal;overflow-wrap:anywhere}.al-popover-content .al-grid-item-text{flex-direction:column;align-items:center;justify-content:center;gap:2px;max-width:100%;min-height:26px}.al-popover-content .al-grid-item-status-text{font-size:10px;line-height:1.1}.al-popover-content .al-badge-list{display:none}.al-drawer-overlay{position:fixed;inset:0;z-index:100000;background:var(--al-drawer-overlay)}.al-drawer-content{-webkit-font-smoothing:antialiased;position:fixed;top:0;right:0;bottom:0;z-index:100001;width:100vw;background:var(--al-bg);overflow-x:hidden;overflow-y:hidden;display:flex;flex-direction:column;box-sizing:border-box}.al-drawer-body{flex:1;overflow-y:auto;overscroll-behavior-y:contain;min-height:0}.al-drawer-content .al-menu{display:flex;flex-direction:column;min-height:0}.al-drawer-content .al-grid{display:flex;flex-direction:column;grid-template-columns:none;max-width:100%;overflow:hidden;flex-shrink:0;gap:0;row-gap:0;column-gap:0;padding:8px 16px}.al-drawer-content .al-grid-item{width:100%;max-width:100%;gap:8px;padding:16px 12px;border-radius:8px;overflow:hidden;background:transparent}.al-drawer-content .al-grid-item:hover,.al-drawer-content .al-grid-item[data-active=true]{background:var(--al-bg-hover)}.al-drawer-content .al-grid-item-left{gap:8px;flex:1 1 auto;min-width:0;overflow:visible}.al-drawer-content .al-grid-item-icon{width:32px;height:32px;border-radius:6.75px;overflow:visible}.al-drawer-content .al-grid-item-icon img{width:100%;height:100%;object-fit:cover;border-radius:6.75px}.al-drawer-content .al-grid-item-label{font-size:16px;line-height:1.35;letter-spacing:0;font-weight:400}.al-drawer-content .al-grid-item[data-active=true] .al-grid-item-label{font-weight:500}.al-drawer-header{display:flex;align-items:center;justify-content:space-between;padding:16px;flex-shrink:0}.al-drawer-title{font-family:Inter,sans-serif;font-size:16px;font-weight:500;line-height:1.5;letter-spacing:0;color:var(--al-text);margin:0}.al-drawer-close{display:inline-flex;align-items:center;justify-content:center;padding:4px;border:none;background:transparent;border-radius:4px;cursor:pointer;color:var(--al-text-secondary);transition:opacity .15s ease}.al-drawer-close:hover{opacity:.7}.al-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:4px;padding:8px}.al-grid-item{display:flex;align-items:center;justify-content:flex-start;gap:10px;padding:16px 12px;border:none;background:var(--al-bg);border-radius:8px;cursor:pointer;transition:none;color:var(--al-text);text-align:left;font-family:Inter,sans-serif;flex-shrink:0}.al-grid-item:hover,.al-grid-item[data-active=true]{background:var(--al-bg-hover)!important}.al-grid-item-left{display:flex;align-items:center;gap:8px;min-width:0;overflow:visible}.al-grid-item-text{display:flex;flex-direction:column;align-items:flex-start;gap:1px;max-width:100%;min-width:0}.al-grid-item-icon{display:flex;align-items:center;justify-content:center;position:relative;width:24px;height:24px;overflow:visible;border-radius:5px;flex-shrink:0}.al-grid-item-icon img{width:100%;height:100%;object-fit:contain;border-radius:inherit}.al-grid-item-label{font-family:Inter,sans-serif;font-size:16px;font-weight:500;line-height:150%;letter-spacing:0;color:var(--al-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.al-grid-item-status-text{font-family:Inter,sans-serif;font-size:12px;font-weight:400;line-height:1.15;letter-spacing:0;color:var(--al-text);white-space:nowrap}.al-status-ribbon{position:absolute;top:-6px;right:-6px;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;box-sizing:border-box;border-radius:999px;color:#fff;font-family:Pretendard,Inter,sans-serif;font-size:12px;font-weight:800;line-height:1;pointer-events:none;box-shadow:0 1px 3px #0000002e}.al-status-ribbon-new{background:var(--al-new-ribbon-bg)}.al-badge{display:flex;padding:1px 4px 2px;justify-content:center;align-items:center;gap:2px;border-radius:3px;background:var(--al-badge-bg);color:var(--al-badge-text);font-family:Pretendard,Inter,sans-serif;font-size:10px;font-weight:600;line-height:150%;letter-spacing:0;white-space:nowrap;flex-shrink:0}\\n\")","import styleInject from '#style-inject';styleInject(\"\")","\"use client\";\n\nimport * as React from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { Drawer } from \"vaul\";\nimport { useAppLauncherContext } from \"../providers/app-launcher-provider\";\nimport type { AppLauncherTriggerStyle } from \"../types\";\nimport { DotsGridIcon } from \"./icons\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\n\nexport interface AppLauncherTriggerProps {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  /** Additional CSS class merged with built-in `.al-trigger-btn` (default trigger only) */\n  className?: string;\n  /** Inline style + CSS custom property overrides (`AppLauncherTriggerStyle`) — default trigger only */\n  style?: AppLauncherTriggerStyle;\n}\n\nexport function AppLauncherTrigger({\n  asChild,\n  children,\n  className,\n  style,\n}: AppLauncherTriggerProps) {\n  const { isMobile, theme } = useAppLauncherContext();\n\n  const hasChildren = children != null;\n  const shouldAsChild = asChild ?? true;\n  const classes = className ? `al-trigger-btn ${className}` : \"al-trigger-btn\";\n\n  const triggerContent = hasChildren ? (\n    children\n  ) : (\n    <button\n      type=\"button\"\n      className={classes}\n      data-theme={theme}\n      data-track=\"open\"\n      style={style}\n    >\n      <DotsGridIcon size={18} />\n    </button>\n  );\n\n  if (isMobile) {\n    return (\n      <TrackingBoundary feature=\"app_launcher\">\n        <Drawer.Trigger asChild={shouldAsChild}>{triggerContent}</Drawer.Trigger>\n      </TrackingBoundary>\n    );\n  }\n\n  return (\n    <TrackingBoundary feature=\"app_launcher\">\n      <Popover.Trigger asChild={shouldAsChild}>{triggerContent}</Popover.Trigger>\n    </TrackingBoundary>\n  );\n}\n","export function DotsGridIcon({ size = 18 }: { size?: number }) {\n  return (\n    <svg\n      className=\"al-trigger-icon\"\n      width={size}\n      height={size}\n      viewBox=\"0 0 18 18\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <rect width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect x=\"7\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect x=\"14\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect y=\"7\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect x=\"7\" y=\"7\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect x=\"14\" y=\"7\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect y=\"14\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect x=\"7\" y=\"14\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n      <rect x=\"14\" y=\"14\" width=\"4\" height=\"4\" rx=\"2\" fill=\"currentColor\" />\n    </svg>\n  );\n}\n\nexport function BackArrowIcon() {\n  return (\n    <svg\n      width=\"24\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M12 19L5 12M5 12L12 5M5 12H19\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function CloseIcon() {\n  return (\n    <svg\n      width=\"20\"\n      height=\"20\"\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M15 5L5 15M5 5L15 15\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { Drawer } from \"vaul\";\nimport { useAppLauncherContext } from \"../providers/app-launcher-provider\";\nimport { useGlobalMenu } from \"../queries/use-global-menu\";\nimport type { GlobalMenuItem } from \"../types\";\nimport {\n  getGlobalMenuItemStatus,\n  getSortedGlobalMenuItems,\n  resolveGlobalMenuItemAssetUrl,\n} from \"../menu\";\nimport { BackArrowIcon, CloseIcon } from \"./icons\";\nimport { cn } from \"../../../lib/utils\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\n\nexport interface AppLauncherContentProps {\n  align?: \"start\" | \"center\" | \"end\";\n  sideOffset?: number;\n  className?: string;\n}\n\nfunction isStakingItem(item: GlobalMenuItem) {\n  return item.id === \"cross-staking\" || item.type === \"cross-staking\";\n}\n\nexport function AppLauncherContent({\n  align = \"end\",\n  sideOffset = 8,\n  className,\n}: AppLauncherContentProps) {\n  const { env, theme, isMobile, domain, close } =\n    useAppLauncherContext();\n  const { data } = useGlobalMenu(env);\n  const menuItems = React.useMemo(\n    () => getSortedGlobalMenuItems(data?.items ?? []),\n    [data?.items],\n  );\n\n  /**\n   * 드로어를 스와이프하다 손가락이 항목 위에서 떨어졌을 때를 걸러내는 임계값.\n   *\n   * 브라우저는 자체 tap slop(모바일 보통 10~25px)을 넘지 않았을 때만 `click` 을\n   * 발생시킨다 — 즉 여기 도달한 시점에 이미 \"탭\"으로 판정된 것이다. 예전 값(8px)은\n   * 그보다 엄격해서, 손가락이 조금만 흔들려도 브라우저가 보낸 클릭을 우리가 다시\n   * 버렸다(모바일에서 가끔 눌리지 않던 원인).\n   */\n  const DRAG_THRESHOLD = 24;\n  const pointerStart = React.useRef<{ x: number; y: number } | null>(null);\n  const wasDrag = React.useRef(false);\n\n  const handlePointerDown = React.useCallback((e: React.PointerEvent) => {\n    pointerStart.current = { x: e.clientX, y: e.clientY };\n    wasDrag.current = false;\n  }, []);\n\n  // 판정은 pointerup 에서 끝낸다 — click 은 pointerup 뒤에 오므로 여기서 좌표를\n  // 지우면 click 시점엔 비교할 게 남지 않는다.\n  const handlePointerUp = React.useCallback((e: React.PointerEvent) => {\n    const start = pointerStart.current;\n    pointerStart.current = null;\n    if (!start) return;\n    wasDrag.current =\n      Math.abs(e.clientX - start.x) > DRAG_THRESHOLD ||\n      Math.abs(e.clientY - start.y) > DRAG_THRESHOLD;\n  }, []);\n\n  // 스크롤·스와이프로 취소된 포인터는 클릭이 아니다.\n  const handlePointerCancel = React.useCallback(() => {\n    pointerStart.current = null;\n    wasDrag.current = true;\n  }, []);\n\n  const currentPathname =\n    typeof window !== \"undefined\" ? window.location.pathname : \"\";\n\n  const handleItemClick = React.useCallback(\n    (e: React.MouseEvent, item: GlobalMenuItem) => {\n      // `detail === 0` 은 키보드·스크린리더가 만든 클릭이다(좌표가 없으므로\n      // 드래그 판정을 적용하면 안 된다).\n      const dragged = wasDrag.current;\n      wasDrag.current = false;\n      if (dragged && e.detail !== 0) return;\n\n      const url = item.url[env] ?? item.url.production;\n      const isExternal = isExternalLink(url, domain);\n\n      if (!isExternal) {\n        try {\n          // 쿼리·해시·하위 경로가 하나라도 다르면 이동해야 한다.\n          // 완전히 같은 페이지일 때만 리로드 대신 메뉴만 닫는다.\n          const target = new URL(url);\n          const isSamePage =\n            target.pathname === window.location.pathname &&\n            target.search === window.location.search &&\n            target.hash === window.location.hash;\n          if (isSamePage) {\n            close();\n            return;\n          }\n        } catch {}\n      }\n\n      if (isExternal) {\n        // 인앱 브라우저/웹뷰는 window.open 을 조용히 차단하는 경우가 있다\n        // (반환값 null). 그때는 아래 같은 창 이동으로 폴백한다.\n        // 주의: feature 에 noopener 를 넣으면 성공해도 반환값이 null 이라\n        // (스펙 동작) 차단 여부를 알 수 없다 — 열린 창의 opener 를 직접 끊는다.\n        const opened = window.open(url, \"_blank\");\n        if (opened) {\n          opened.opener = null;\n          close();\n          return;\n        }\n      }\n\n      // 같은 창 이동에서는 close() 를 부르면 안 된다 — useHistoryDismiss 가\n      // 닫힘을 감지하고 history.go(-1) 로 되감으면서 방금 시작된 네비게이션을\n      // 취소해 버린다(1.3.15 회귀: 메뉴만 닫히고 이동이 안 되던 원인).\n      // 페이지가 언로드되면서 메뉴도 함께 사라진다.\n      window.location.href = url;\n    },\n    [env, domain, close],\n  );\n\n  const renderMenuItem = (item: GlobalMenuItem) => {\n    const url = item.url[env] ?? item.url.production;\n    const isExternal = isExternalLink(url, domain);\n    const serviceStatus = getGlobalMenuItemStatus(item);\n    const isEndingSoon = serviceStatus === \"ending\";\n    const iconUrl = resolveGlobalMenuItemAssetUrl(item.iconUrl, env);\n    let isActive = false;\n    if (!isExternal) {\n      try {\n        const urlPathname = new URL(url).pathname;\n        isActive =\n          currentPathname === urlPathname ||\n          currentPathname.startsWith(urlPathname + \"/\");\n      } catch {\n        isActive = false;\n      }\n    }\n\n    return (\n      <button\n        key={item.id}\n        type=\"button\"\n        className=\"al-grid-item\"\n        data-track=\"menu_item_click\"\n        data-track-id={item.id}\n        data-active={isActive || undefined}\n        data-service-status={serviceStatus}\n        onPointerDown={handlePointerDown}\n        onPointerUp={handlePointerUp}\n        onPointerCancel={handlePointerCancel}\n        onClick={(e) => handleItemClick(e, item)}\n      >\n        <div className=\"al-grid-item-left\">\n          <div className=\"al-grid-item-icon\">\n            <img\n              src={iconUrl}\n              alt={item.label}\n              width={24}\n              height={24}\n              loading=\"lazy\"\n            />\n            {item.isNew && (\n              <span className=\"al-status-ribbon al-status-ribbon-new\">N</span>\n            )}\n          </div>\n          <span className=\"al-grid-item-text\">\n            <span className=\"al-grid-item-label\">{item.label}</span>\n            {isEndingSoon && (\n              <span className=\"al-grid-item-status-text\">(End Soon)</span>\n            )}\n            {item.badge && !isStakingItem(item) && (\n              <span className=\"al-badge al-badge-list\">{item.badge}</span>\n            )}\n          </span>\n        </div>\n      </button>\n    );\n  };\n\n  const menuContent = (\n    <div className=\"al-menu\" data-theme={theme}>\n      <div className=\"al-grid\">{menuItems.map(renderMenuItem)}</div>\n    </div>\n  );\n\n  if (isMobile) {\n    return (\n      <TrackingBoundary feature=\"app_launcher\">\n        <Drawer.Portal>\n          <Drawer.Overlay className=\"al-drawer-overlay\" />\n          <Drawer.Content\n            className={cn(\"al-drawer-content\", className)}\n            data-theme={theme}\n          >\n            <div className=\"al-drawer-header\">\n              <Drawer.Close className=\"al-drawer-close\" data-track=\"close\">\n                <BackArrowIcon />\n              </Drawer.Close>\n              <Drawer.Title className=\"al-drawer-title\">\n                ONE Services\n              </Drawer.Title>\n              <Drawer.Close className=\"al-drawer-close\" data-track=\"close\">\n                <CloseIcon />\n              </Drawer.Close>\n            </div>\n            <div className=\"al-drawer-body\">{menuContent}</div>\n          </Drawer.Content>\n        </Drawer.Portal>\n      </TrackingBoundary>\n    );\n  }\n\n  return (\n    <TrackingBoundary feature=\"app_launcher\">\n      <Popover.Portal>\n        <Popover.Content\n          align={align}\n          sideOffset={sideOffset}\n          className={cn(\"al-popover-content\", className)}\n          data-theme={theme}\n        >\n          {menuContent}\n        </Popover.Content>\n      </Popover.Portal>\n    </TrackingBoundary>\n  );\n}\n\nfunction isExternalLink(url: string, domain?: string): boolean {\n  try {\n    const linkHostname = new URL(url).hostname;\n    if (typeof window === \"undefined\") return true;\n    return linkHostname !== (domain ?? window.location.hostname);\n  } catch {\n    return false;\n  }\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { GlobalMenu } from \"../types\";\nimport type { Environment } from \"../../../types\";\nimport { GLOBAL_MENU_URLS } from \"../constants\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport { getFallbackGlobalMenu } from \"../fallback-menu\";\n\nexport function useGlobalMenu(env?: Environment) {\n  const resolvedEnv = resolveEnvironment(env);\n\n  return useQuery<GlobalMenu>({\n    queryKey: [\"global-menu\", resolvedEnv],\n    queryFn: async () => {\n      try {\n        const res = await fetch(GLOBAL_MENU_URLS[resolvedEnv], {\n          cache: \"no-store\",\n        });\n        if (!res.ok) {\n          throw new Error(`Failed to fetch global menu: ${res.status}`);\n        }\n        return res.json();\n      } catch {\n        return getFallbackGlobalMenu(resolvedEnv);\n      }\n    },\n    // Static menu fetches are intentionally not aborted when responsive roots\n    // remount. Keeping the in-flight query lets the next observer reuse it.\n    staleTime: 5 * 60 * 1000,\n    gcTime: 10 * 60 * 1000,\n    retry: false,\n    refetchOnMount: true,\n  });\n}\n","import type { Environment } from \"../../types\";\n\nexport const GLOBAL_MENU_URLS: Record<Environment, string> = {\n  dev: \"https://stg-contents.crosstoken.io/frontend/common/app-launcher/app-launcher-v2.json\",\n  stage: \"https://stg-contents.crosstoken.io/frontend/common/app-launcher/app-launcher-v2.json\",\n  production: \"https://contents.crosstoken.io/frontend/common/app-launcher/app-launcher-v2.json\",\n};\n","import type { GlobalMenu } from \"./types\";\n\nconst STAGE_ICON_BASE =\n  \"https://stg-contents.crosstoken.io/frontend/common/app-launcher/icons\";\nconst PROD_ICON_BASE =\n  \"https://contents.crosstoken.io/frontend/common/app-launcher/icons\";\n\nfunction icon(env: \"stage\" | \"production\", name: string): string {\n  const base = env === \"production\" ? PROD_ICON_BASE : STAGE_ICON_BASE;\n  return `${base}/${name}.png`;\n}\n\nexport function getFallbackGlobalMenu(env: \"dev\" | \"stage\" | \"production\"): GlobalMenu {\n  const iconEnv = env === \"production\" ? \"production\" : \"stage\";\n\n  return {\n    version: \"0.0.1-fallback\",\n    items: [\n      {\n        id: \"gametoken-cross\",\n        label: \"Gametoken\",\n        description: \"Trade game tokens easily using CROSS.\",\n        url: {\n          dev: \"https://stg.onechain.nexus/gametoken\",\n          stage: \"https://stg.onechain.nexus/gametoken\",\n          production: \"https://www.onechain.nexus/gametoken\",\n        },\n        iconUrl: icon(iconEnv, \"game-token\"),\n        order: 1000,\n        type: \"gametoken\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"forge\",\n        label: \"Forge\",\n        description: \"Launch Your Game, Ignite the Market\",\n        url: {\n          dev: \"https://stg.onechain.nexus/forge\",\n          stage: \"https://stg.onechain.nexus/forge\",\n          production: \"https://onechain.nexus/forge\",\n        },\n        iconUrl: icon(iconEnv, \"forge\"),\n        order: 4000,\n        type: \"forge\",\n        badge: null,\n        isNew: true,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"crossd\",\n        label: \"Bridge\",\n        description: \"The first decentralized exchange on the CROSS network.\",\n        url: {\n          dev: \"https://stg.crossdefi.io/swap-bridge\",\n          stage: \"https://stg.crossdefi.io/swap-bridge\",\n          production: \"https://www.crossdefi.io/swap-bridge\",\n        },\n        iconUrl: icon(iconEnv, \"crossd\"),\n        order: 5000,\n        type: \"crossd\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"ending\",\n      },\n      {\n        id: \"onepop\",\n        label: \"ONEpop\",\n        description: \"Send crypto with just an X handle.\",\n        url: {\n          dev: \"https://dev.onechain.nexus/pop\",\n          stage: \"https://stg.onechain.nexus/pop\",\n          production: \"https://onechain.nexus/pop\",\n        },\n        iconUrl:\n          iconEnv === \"production\"\n            ? \"https://contents.onechain.nexus/frontend/common/app-launcher/icons/onepop.png\"\n            : \"https://stg-contents.onechain.nexus/frontend/common/app-launcher/icons/onepop.png\",\n        order: 5500,\n        type: \"onepop\",\n        badge: null,\n        isNew: true,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"prediction\",\n        label: \"Prediction\",\n        description: \"Prediction market\",\n        url: {\n          dev: \"https://stg-prediction.crossdefi.io\",\n          stage: \"https://stg-prediction.crossdefi.io\",\n          production: \"https://prediction.crossdefi.io/\",\n        },\n        iconUrl: icon(iconEnv, \"prediction\"),\n        order: 6000,\n        type: \"prediction\",\n        badge: null,\n        isNew: true,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"rewards\",\n        label: \"Rewards\",\n        description: \"Deposit CROSS and earn rewards. Grow together.\",\n        url: {\n          dev: \"https://stg.onechain.nexus/rewards\",\n          stage: \"https://stg.onechain.nexus/rewards\",\n          production: \"https://onechain.nexus/rewards\",\n        },\n        iconUrl: icon(iconEnv, \"reward\"),\n        order: 7000,\n        type: \"rewards\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"ending\",\n      },\n      {\n        id: \"cross-staking\",\n        label: \"Staking\",\n        description: \"Stake CROSS and earn rewards.\",\n        url: {\n          dev: \"https://stg-stake.ogfcorp.com\",\n          stage: \"https://stg-stake.ogfcorp.com\",\n          production: \"https://stake.ogfcorp.com\",\n        },\n        iconUrl: icon(iconEnv, \"cross-staking\"),\n        order: 7500,\n        type: \"cross-staking\",\n        badge: null,\n        isNew: true,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"nft\",\n        label: \"NFT\",\n        description: \"Trade and collect NFTs in the CROSS ecosystem.\",\n        url: {\n          dev: \"https://stg.crossnft.io/\",\n          stage: \"https://stg.crossnft.io/\",\n          production: \"https://www.crossnft.io/\",\n        },\n        iconUrl: icon(iconEnv, \"nft\"),\n        order: 8000,\n        type: \"nft\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"play\",\n        label: \"Quest\",\n        description: \"Discover and play games on CROSS.\",\n        url: {\n          dev: \"https://stg.onechain.nexus/quest\",\n          stage: \"https://stg.onechain.nexus/quest\",\n          production: \"https://www.onechain.nexus/quest\",\n        },\n        iconUrl: icon(iconEnv, \"play\"),\n        order: 8500,\n        type: \"play\",\n        badge: null,\n        isNew: true,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"shop\",\n        label: \"Shop\",\n        description: \"Game Payment Platform & In-Game Currency Store.\",\n        url: {\n          dev: \"https://stg.onechain.nexus/shop\",\n          stage: \"https://stg.onechain.nexus/shop\",\n          production: \"https://www.onechain.nexus/shop\",\n        },\n        iconUrl: icon(iconEnv, \"shop\"),\n        order: 9000,\n        type: \"shop\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"explorer\",\n        label: \"Explorer\",\n        description: \"View transactions, blocks, and token data on the CROSS network.\",\n        url: {\n          dev: \"https://stg-explorer.onechain.nexus\",\n          stage: \"https://stg-explorer.onechain.nexus\",\n          production: \"https://explorer.onechain.nexus\",\n        },\n        iconUrl: icon(iconEnv, \"explorer\"),\n        order: 12000,\n        type: \"explorer\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"available\",\n      },\n      {\n        id: \"wave\",\n        label: \"Wave\",\n        description: \"Stream, support, and earn in the CROSS ecosystem.\",\n        url: {\n          dev: \"https://stg.onechain.nexus/wave\",\n          stage: \"https://stg.onechain.nexus/wave\",\n          production: \"https://www.onechain.nexus/wave\",\n        },\n        iconUrl: icon(iconEnv, \"wave\"),\n        order: 110000,\n        type: \"wave\",\n        badge: null,\n        isNew: false,\n        serviceStatus: \"available\",\n      },\n    ],\n  };\n}\n","import type { Environment } from \"../../types\";\nimport type {\n  GlobalMenuCategory,\n  GlobalMenuItem,\n  GlobalMenuItemAssetUrl,\n  GlobalMenuItemServiceStatus,\n} from \"./types\";\n\nexport type AppLauncherStatusId = GlobalMenuCategory[\"status\"];\n\nexport interface AppLauncherStatusSection extends GlobalMenuCategory {}\n\nexport interface AppLauncherGroupedStatusSection extends AppLauncherStatusSection {\n  items: GlobalMenuItem[];\n}\n\nexport const APP_LAUNCHER_STATUS_SECTIONS: AppLauncherStatusSection[] = [\n  {\n    status: \"all\",\n    label: \"All\",\n  },\n  {\n    status: \"ending\",\n    label: \"Ending Soon\",\n  },\n];\n\nconst APP_LAUNCHER_STATUS_IDS = new Set<AppLauncherStatusId>([\n  \"all\",\n  \"available\",\n  \"ending\",\n  \"comingSoon\",\n]);\n\nexport function getAppLauncherStatusSections(\n  categories?: readonly GlobalMenuCategory[] | null,\n): AppLauncherStatusSection[] {\n  const configuredSections =\n    categories\n      ?.filter((category): category is AppLauncherStatusSection => {\n        return (\n          APP_LAUNCHER_STATUS_IDS.has(category.status) &&\n          typeof category.label === \"string\" &&\n          category.label.trim().length > 0\n        );\n      })\n      .map((category) => ({\n        status: category.status,\n        label: category.label.trim(),\n      })) ?? [];\n\n  return configuredSections.length > 0\n    ? configuredSections\n    : APP_LAUNCHER_STATUS_SECTIONS;\n}\n\nexport function getGlobalMenuItemStatus(\n  item: GlobalMenuItem,\n): GlobalMenuItemServiceStatus {\n  if (item.serviceStatus === \"ending\" || item.serviceStatus === \"comingSoon\") {\n    return item.serviceStatus;\n  }\n  return \"available\";\n}\n\nexport function resolveGlobalMenuItemAssetUrl(\n  assetUrl: GlobalMenuItemAssetUrl,\n  env: Environment,\n): string {\n  if (typeof assetUrl === \"string\") {\n    return assetUrl;\n  }\n\n  return assetUrl[env] ?? assetUrl.production ?? assetUrl.stage ?? assetUrl.dev;\n}\n\nexport function getSortedGlobalMenuItems(\n  items: readonly GlobalMenuItem[],\n): GlobalMenuItem[] {\n  return [...items].sort((a, b) => {\n    const statusDelta = getAllMenuSortRank(a) - getAllMenuSortRank(b);\n    if (statusDelta !== 0) return statusDelta;\n    return a.order - b.order;\n  });\n}\n\nexport function groupGlobalMenuItemsByStatus(\n  items: GlobalMenuItem[],\n  sections: readonly AppLauncherStatusSection[] = APP_LAUNCHER_STATUS_SECTIONS,\n): AppLauncherGroupedStatusSection[] {\n  const sortedItems = getSortedGlobalMenuItems(items);\n\n  return getAppLauncherStatusSections(sections).map((section) => ({\n    ...section,\n    items:\n      section.status === \"all\"\n        ? sortedItems\n        : sortedItems.filter(\n            (item) => getGlobalMenuItemStatus(item) === section.status,\n          ),\n  })).filter((section) => section.items.length > 0);\n}\n\nfunction getAllMenuSortRank(item: GlobalMenuItem): number {\n  const status = getGlobalMenuItemStatus(item);\n  if (status === \"comingSoon\") return 0;\n  if (status === \"ending\") return 2;\n  return 1;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { Drawer } from \"vaul\";\nimport {\n  WalletInfoContext,\n  type WalletInfoView,\n} from \"../providers/wallet-info-provider\";\nimport type { DrawerDirection, Environment, Theme } from \"../../../types\";\nimport { type ConnectorId, CONNECTOR_REGISTRY, type PreferredToken, type TokenBalance, type WalletInfoStyle } from \"../types\";\nimport type {\n  EstimateGasFn,\n  GetTransactionReceiptFn,\n  LpBalanceReaderFn,\n  PortfolioSection,\n  SendTransactionFn,\n  StakingRewardsReaderFn,\n} from \"../../wallet-portfolio/types\";\nimport {\n  clearRecentSendAddresses,\n  type SendAccount,\n  type SendAsset,\n} from \"../../send\";\nimport type {\n  BridgeApproveFn,\n  BridgeHistoryItem,\n  BridgeGetApprovalFn,\n  BridgeGetToTokensFn,\n  BridgeQuoteFn,\n  BridgeSubmitFn,\n  BridgeToken,\n} from \"../../bridge\";\nimport type { OnePopSummary } from \"../../onepop\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport { DEFAULT_TERMS_URL, DEFAULT_PRIVACY_URL } from \"../../../lib/constants\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport { useHistoryDismiss } from \"../../../hooks/use-history-dismiss\";\nimport { useMediaQuery } from \"../../../hooks/use-media-query\";\nimport { setDappUiAnalyticsUser } from \"../../../lib/sentry/index\";\nimport { WalletInfoTrigger } from \"./WalletInfoTrigger\";\nimport { WalletInfoContent } from \"./WalletInfoContent\";\nimport { WalletInfoNav } from \"./WalletInfoNav\";\nimport { WalletInfoFooter } from \"./WalletInfoFooter\";\nimport \"../styles/theme.css\";\nimport \"../styles/base.css\";\nimport \"../styles/keyframes.css\";\nimport \"../styles/component.css\";\nimport \"../styles/token-detail.css\";\nimport \"../styles/utilities.css\";\n\nexport interface WalletInfoProps {\n  env?: Environment;\n  theme?: Theme;\n  mobileBreakpoint?: number;\n  drawerDirection?: DrawerDirection;\n  modal?: boolean;\n  showBalance?: boolean;\n  showForgeToken?: boolean;\n  showGameToken?: boolean;\n  showQR?: boolean;\n  /**\n   * Bridge 액션 노출 여부 (기본 true).\n   * - true: 액션 행에 Bridge 버튼, Receive는 상단 QR 버튼으로 표기\n   * - false: 액션 행에 Receive 버튼, 상단 QR 버튼 미표기\n   */\n  showBridge?: boolean;\n  qrLogoSrc?: string;\n  walletAddress: string;\n  accountName?: string;\n  sendAccounts?: SendAccount[];\n  /** 헤더 좌측에 표시될 프로필 이미지 URL. 미지정 시 주소 기반 그라디언트 아바타가 자동 생성됨. */\n  profileImageUrl?: string;\n  /** Resolves connectorName & connectorIconUrl from CONNECTOR_REGISTRY */\n  connectorId?: ConnectorId;\n  connectorName?: string;\n  connectorIconUrl?: string;\n  preferredTokens?: PreferredToken[];\n  onSelectWallet?: () => void;\n  onCopyAddress?: (address: string, success: boolean) => void;\n  /**\n   * WalletInfo 기본 액션 row의 Buy 클릭 핸들러. Promise를 반환하면 reject 시\n   * Buy 카드가 `onBuyDisabledMessage`와 동일한 토스트로 실패를 안내한다.\n   */\n  onBuy?: () => void | Promise<void>;\n  /**\n   * Buy 카드를 시각적으로 비활성 상태로 표시하고, 사용자가 클릭하면 이\n   * 메시지를 토스트로 띄운다. `onBuy`보다 우선. 예: on-ramp가 사용자 국가에서\n   * 차단된 경우 안내 메시지 전달용.\n   */\n  onBuyDisabledMessage?: string;\n  /**\n   * Bridge 카드를 시각적으로 비활성 상태로 표시하고, 클릭하면 이 메시지를\n   * 토스트로 띄운다. 예: 연결된 지갑이 CROSSx 계열이 아니라 브릿지를\n   * 지원하지 않는 경우.\n   */\n  onBridgeDisabledMessage?: string;\n  /**\n   * Send 카드(및 토큰 행 탭으로 진입하는 전송 플로우)를 시각적으로 비활성\n   * 상태로 표시하고, 클릭하면 이 메시지를 토스트로 띄운다. 예: 연결된 지갑이\n   * CROSSx 계열이 아닌 경우.\n   */\n  onSendDisabledMessage?: string;\n  /**\n   * 지정 시 기본 Disconnect 버튼이 Footer에 노출됨. `WalletInfo.Footer` 슬롯이\n   * 있으면 해당 슬롯이 메인 영역을 대체한다. Terms/Privacy 링크는 둘 중 무엇을\n   * 쓰든 항상 하단에 함께 렌더된다.\n   */\n  onDisconnect?: () => void;\n  /** 기본 Disconnect 버튼 라벨 (기본 \"Disconnect\"). */\n  disconnectLabel?: string;\n  /**\n   * Footer 하단 Terms 링크 URL.\n   * 미지정 시 CROSS 에코시스템 기본 URL로 폴백되어 자동 노출된다.\n   * 숨기려면 빈 문자열(`\"\"`)을 전달.\n   */\n  termsUrl?: string;\n  /** Terms 링크 라벨 (기본 \"Terms of Service\"). */\n  termsLabel?: string;\n  /**\n   * Footer 하단 Privacy 링크 URL.\n   * 미지정 시 CROSS 에코시스템 기본 URL로 폴백되어 자동 노출된다.\n   * 숨기려면 빈 문자열(`\"\"`)을 전달.\n   */\n  privacyUrl?: string;\n  /** Privacy 링크 라벨 (기본 \"Privacy Policy\"). */\n  privacyLabel?: string;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /**\n   * Portfolio 뷰 사용 여부.\n   * true면 기본 액션 row에 Portfolio 버튼이 표시되며, 클릭 시 내부 Portfolio 뷰로 전환됩니다.\n   */\n  showPortfolio?: boolean;\n  /**\n   * ONEpop 사용 여부 (기본 **false** — 서비스 오픈 전 딥링크가 죽은 링크가\n   * 되지 않도록 opt-in). true면 기본 액션 row에 ONEpop 버튼이 표시되며,\n   * 클릭 시 내부 ONEpop 뷰로 전환된다. 뷰는 순수 UI라 아래 데이터/콜백이\n   * 없어도 렌더된다 — 액션 버튼은 전부 ONEpop 서비스 딥링크가 기본 동작이다.\n   * connect-kit-react 경유라면 config `onePopEnabled: true` 하나로 노출과\n   * 조회가 함께 켜진다.\n   */\n  showOnePop?: boolean;\n  /**\n   * ONEpop 뷰가 표시할 상태(수령 대기 수/합계, replies 카운트, 최근 활동).\n   *\n   * dapp-ui는 `@nexus-cross/pop`(viem 필수)에 의존할 수 없으므로 one-pop-api를\n   * 직접 조회하지 않는다 — `/drops`·`/histories`는 SIWE JWT + X 연결이 필요해\n   * 서명 가능한 호스트가 조회한 뒤 이 prop 으로 주입한다.\n   * `@nexus-cross/connect-kit-react`를 쓰면 자동 주입된다.\n   * 미주입 시 \"내역 없음\" 온보딩 화면을 그린다.\n   */\n  onePopSummary?: OnePopSummary;\n  /**\n   * ONEpop 뷰 진입 시 1회 호출. `onePopSummary` 조회는 SIWE 서명(지갑 팝업)을\n   * 요구하므로, 주입측이 사용자가 실제로 ONEpop을 열 때까지 조회를 미루는\n   * 트리거로 쓴다.\n   */\n  onOnePopOpen?: () => void;\n  /** Send POP 카드 + \"Send your first POP!\" CTA. 미주입 시 비활성. */\n  onOnePopSend?: () => void;\n  /** Claim POPs 카드 + 수령 대기 배너. 미주입 시 비활성. */\n  onOnePopClaim?: () => void;\n  /** Activity 카드 + \"See all activity\" + replies 배너. 미주입 시 비활성. */\n  onOnePopActivity?: () => void;\n  /**\n   * 활동 화면으로 나가는 경로에서 추가로 호출되는 알림 훅(목적지 이동은 그대로).\n   * `onOnePopActivity` 와 달리 기본 동작을 대체하지 않는다 — 답장을 확인\n   * 처리해 `onePopSummary.repliesWaitingCount` 를 떨어뜨리는 용도다.\n   * connect-kit 경유라면 `useOnePopSummary().markRepliesSeen` 이 자동 배선된다.\n   */\n  onOnePopRepliesSeen?: () => void;\n  /**\n   * ONEpop Available 카드의 \"Get ONEUSD ›\" 자리를 대체할 노드.\n   *\n   * 지갑을 떠나지 않고 Get ONEUSD 위젯(Swap / Bridge / Transfer 선택 모달)을\n   * 열기 위한 슬롯이다 — 위젯은 wagmi 어댑터가 필요하므로 dapp-ui 가 직접\n   * 마운트하지 못한다. `@nexus-cross/connect-kit-react` 를 쓰면\n   * `<CrossGetOneUsd mode=\"button\" buttonComponent={OnePopGetTokenTrigger}>` 가\n   * 자동 주입되고, 직접 쓰는 호스트는 같은 형태로 넘기면 된다.\n   * 미주입 시 기존 게임토큰 브리지 딥링크가 그대로 동작한다.\n   */\n  onePopGetTokenSlot?: React.ReactNode;\n  /** ONEpop 워드마크 이미지 URL. 미지정 시 내장 SVG 재현본. */\n  onePopLogoSrc?: string;\n  /** ONEpop 히어로 일러스트 URL. 미지정 시 내장 SVG 재현본. */\n  onePopHeroSrc?: string;\n  /** Portfolio 뷰의 헤더 타이틀 (기본 \"My Portfolio\"). */\n  portfolioTitle?: string;\n  /**\n   * Portfolio 뷰에 노출할 섹션 필터. 미지정 시 모든 섹션을 기본 순서대로\n   * 표시하고, 배열을 주면 포함된 섹션만 표시한다(빈 배열 = 전부 숨김).\n   * 예: `[\"rewards\", \"staking\"]`.\n   */\n  portfolioSections?: PortfolioSection[];\n  /** Total Assets 섹션 노출 여부 (기본 true). showBalance=true일 때만 실제 표기. */\n  showTotalAssets?: boolean;\n  /** Total Assets 라벨 텍스트 (기본 \"Total Assets USD\"). */\n  totalAssetsLabel?: string;\n  /**\n   * Send 페이지의 일반 토큰 전송에 사용할 외부 트랜잭션 전송 함수.\n   * wagmi의 `sendTransactionAsync`를 그대로 전달해도 호환된다.\n   */\n  sendTransaction?: SendTransactionFn;\n  getTransactionReceipt?: GetTransactionReceiptFn;\n  /**\n   * Send 확인 단계에서 표시할 가스/수수료 추정 함수.\n   * 주입되지 않으면 확인 화면의 Gas/Est.Time/Max.Gas 행은 \"—\"로 표시된다.\n   */\n  estimateGas?: EstimateGasFn;\n  /**\n   * Terms/Privacy · 포트폴리오 섹션 등 외부 링크 이동을 가로채는 콜백.\n   * `(url, ctx) => newUrl | null | undefined | Promise<...>` 형태이며\n   *   - `string`      → 해당 URL로 이동(변형 가능)\n   *   - `null`        → 이동 취소\n   *   - `undefined`   → 원본 URL 유지\n   * middle-click · cmd-click은 가로채지 않고 원본 `href`로 열린다.\n   * `showPortfolio=true`일 때 내부 `WalletPortfolioBody`로도 릴레이된다.\n   */\n  onOutlink?: OnOutlink;\n  /**\n   * game-swap LP 섹션의 사용자별 LP 잔고/지분율을 온체인에서 보강하기 위한\n   * 주입형 reader. `showPortfolio=true`일 때 `WalletPortfolioBody`로 릴레이된다.\n   * 미주입 시 LP 섹션은 풀 정보만 표시한다.\n   */\n  lpBalanceReader?: LpBalanceReaderFn;\n  /**\n   * CROSS Staking 섹션의 온체인 `earned()` / `balanceOf()`를 보강하기 위한\n   * 주입형 reader. `showPortfolio=true`일 때 `WalletPortfolioBody`로 릴레이된다.\n   * 미주입 시 Staking 섹션은 API `cumulative_earned`로 폴백한다.\n   */\n  stakingRewardsReader?: StakingRewardsReaderFn;\n  bridgeTokens?: BridgeToken[];\n  bridgeHistory?: BridgeHistoryItem[];\n  getBridgeQuote?: BridgeQuoteFn;\n  getBridgeToTokens?: BridgeGetToTokensFn;\n  getBridgeApproval?: BridgeGetApprovalFn;\n  approveBridge?: BridgeApproveFn;\n  submitBridge?: BridgeSubmitFn;\n  /**\n   * 상단 QR 버튼 / 기본 액션 row의 Receive / Send 콜백. (Buy는 위 onBuy\n   * prop으로 이미 정의됨.)\n   */\n  onReceive?: () => void;\n  /**\n   * @deprecated Bridge 버튼은 항상 apps.json(gametokenBridge) 웹으로\n   * 이동한다. 이 콜백은 더 이상 호출되지 않는다.\n   */\n  onBridge?: () => void;\n  onSend?: () => void;\n  style?: WalletInfoStyle;\n  children: React.ReactNode;\n}\n\nfunction WalletInfoRoot({\n  env,\n  theme = \"dark\",\n  mobileBreakpoint = 768,\n  drawerDirection,\n  modal,\n  showBalance = false,\n  showForgeToken = false,\n  showGameToken = true,\n  showQR = true,\n  showBridge = true,\n  qrLogoSrc,\n  walletAddress,\n  accountName,\n  sendAccounts,\n  profileImageUrl,\n  connectorId,\n  connectorName: connectorNameProp,\n  connectorIconUrl: connectorIconUrlProp,\n  preferredTokens = [],\n  onSelectWallet,\n  onCopyAddress,\n  onBuy,\n  onBuyDisabledMessage,\n  onBridgeDisabledMessage,\n  onSendDisabledMessage,\n  onDisconnect,\n  disconnectLabel = \"Disconnect\",\n  termsUrl = DEFAULT_TERMS_URL,\n  termsLabel = \"Terms of Service\",\n  privacyUrl = DEFAULT_PRIVACY_URL,\n  privacyLabel = \"Privacy Policy\",\n  open: propOpen,\n  onOpenChange,\n  showPortfolio = false,\n  showOnePop = false,\n  onePopSummary,\n  onOnePopOpen,\n  onOnePopSend,\n  onOnePopClaim,\n  onOnePopActivity,\n  onOnePopRepliesSeen,\n  onePopGetTokenSlot,\n  onePopLogoSrc,\n  onePopHeroSrc,\n  portfolioTitle = \"My Portfolio\",\n  portfolioSections,\n  showTotalAssets = true,\n  totalAssetsLabel = \"Total Assets USD\",\n  sendTransaction,\n  getTransactionReceipt,\n  estimateGas,\n  onOutlink,\n  lpBalanceReader,\n  stakingRewardsReader,\n  bridgeTokens,\n  bridgeHistory,\n  getBridgeQuote,\n  getBridgeToTokens,\n  getBridgeApproval,\n  approveBridge,\n  submitBridge,\n  onReceive,\n  onBridge,\n  onSend,\n  style,\n  children,\n}: WalletInfoProps) {\n  const resolvedEnv = resolveEnvironment(env);\n  const registryMeta = connectorId ? CONNECTOR_REGISTRY[connectorId] : undefined;\n  const connectorName = connectorNameProp ?? registryMeta?.name;\n  const connectorIconUrl = connectorIconUrlProp ?? registryMeta?.iconUrl;\n\n  const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint}px)`);\n  const resolvedDirection =\n    drawerDirection ?? (showBalance ? \"right\" : \"bottom\");\n  const isSideDrawer =\n    resolvedDirection === \"left\" || resolvedDirection === \"right\";\n  const resolvedModal = modal ?? false;\n  const isControlled = propOpen !== undefined;\n  const [internalOpen, setInternalOpen] = React.useState(false);\n  const open = isControlled ? propOpen : internalOpen;\n\n  const handleOpenChange = React.useCallback(\n    (next: boolean) => {\n      if (!isControlled) setInternalOpen(next);\n      onOpenChange?.(next);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  const close = React.useCallback(\n    () => handleOpenChange(false),\n    [handleOpenChange],\n  );\n\n  // Android 백버튼 / iOS 백스와이프로 닫히게 한다.\n  useHistoryDismiss(open, close);\n\n  const [navContent, setNavContent] = React.useState<React.ReactNode | null>(\n    null,\n  );\n  const [navPosition, setNavPosition] = React.useState<\"top\" | \"bottom\">(\"top\");\n  const [footerContent, setFooterContent] =\n    React.useState<React.ReactNode | null>(null);\n  const [view, setView] = React.useState<WalletInfoView>(\"wallet\");\n  const [selectedSendToken, setSelectedSendToken] =\n    React.useState<SendAsset | null>(null);\n  const [selectedDetailToken, setSelectedDetailToken] =\n    React.useState<TokenBalance | null>(null);\n\n  React.useEffect(() => {\n    if (!open) {\n      setView(\"wallet\");\n      setSelectedSendToken(null);\n      setSelectedDetailToken(null);\n    }\n  }, [open]);\n\n  React.useEffect(() => {\n    setDappUiAnalyticsUser(walletAddress);\n  }, [walletAddress]);\n\n  const handleDisconnect = React.useCallback(() => {\n    clearRecentSendAddresses(walletAddress);\n    onDisconnect?.();\n  }, [onDisconnect, walletAddress]);\n\n  const contextValue = React.useMemo(\n    () => ({\n      env: resolvedEnv,\n      theme,\n      isMobile,\n      open,\n      drawerDirection: resolvedDirection,\n      modal: resolvedModal,\n      showBalance,\n      showForgeToken,\n      showGameToken,\n      showQR,\n      showBridge,\n      qrLogoSrc,\n      walletAddress,\n      accountName,\n      sendAccounts,\n      profileImageUrl,\n      connectorName,\n      connectorIconUrl,\n      preferredTokens,\n      close,\n      onSelectWallet,\n      onCopyAddress,\n      onBuy,\n      onBuyDisabledMessage,\n      onBridgeDisabledMessage,\n      onSendDisabledMessage,\n      onDisconnect: onDisconnect ? handleDisconnect : undefined,\n      disconnectLabel,\n      termsUrl,\n      termsLabel,\n      privacyUrl,\n      privacyLabel,\n      portalStyle: style,\n      navContent,\n      navPosition,\n      setNavContent,\n      setNavPosition,\n      footerContent,\n      setFooterContent,\n      view,\n      setView,\n      selectedSendToken,\n      setSelectedSendToken,\n      selectedDetailToken,\n      setSelectedDetailToken,\n      showPortfolio,\n      showOnePop,\n      onePopSummary,\n      onOnePopOpen,\n      onOnePopSend,\n      onOnePopClaim,\n      onOnePopActivity,\n      onOnePopRepliesSeen,\n      onePopGetTokenSlot,\n      onePopLogoSrc,\n      onePopHeroSrc,\n      portfolioTitle,\n      portfolioSections,\n      showTotalAssets,\n      totalAssetsLabel,\n      sendTransaction,\n      getTransactionReceipt,\n      estimateGas,\n      onOutlink,\n      lpBalanceReader,\n      stakingRewardsReader,\n      bridgeTokens,\n      bridgeHistory,\n      getBridgeQuote,\n      getBridgeToTokens,\n      getBridgeApproval,\n      approveBridge,\n      submitBridge,\n      onReceive,\n      onBridge,\n      onSend,\n    }),\n    [\n      resolvedEnv,\n      theme,\n      isMobile,\n      open,\n      resolvedDirection,\n      resolvedModal,\n      showBalance,\n      showForgeToken,\n      showGameToken,\n      showQR,\n      showBridge,\n      qrLogoSrc,\n      walletAddress,\n      accountName,\n      sendAccounts,\n      profileImageUrl,\n      connectorName,\n      connectorIconUrl,\n      preferredTokens,\n      close,\n      onSelectWallet,\n      onCopyAddress,\n      onBuy,\n      onBuyDisabledMessage,\n      onBridgeDisabledMessage,\n      onSendDisabledMessage,\n      onDisconnect,\n      handleDisconnect,\n      disconnectLabel,\n      termsUrl,\n      termsLabel,\n      privacyUrl,\n      privacyLabel,\n      style,\n      navContent,\n      navPosition,\n      footerContent,\n      view,\n      selectedSendToken,\n      selectedDetailToken,\n      showPortfolio,\n      showOnePop,\n      onePopSummary,\n      onOnePopOpen,\n      onOnePopSend,\n      onOnePopClaim,\n      onOnePopActivity,\n      onOnePopRepliesSeen,\n      onePopGetTokenSlot,\n      onePopLogoSrc,\n      onePopHeroSrc,\n      portfolioTitle,\n      portfolioSections,\n      showTotalAssets,\n      totalAssetsLabel,\n      sendTransaction,\n      getTransactionReceipt,\n      estimateGas,\n      onOutlink,\n      lpBalanceReader,\n      stakingRewardsReader,\n      bridgeTokens,\n      bridgeHistory,\n      getBridgeQuote,\n      getBridgeToTokens,\n      getBridgeApproval,\n      approveBridge,\n      submitBridge,\n      onReceive,\n      onBridge,\n      onSend,\n    ],\n  );\n\n  return (\n    <WalletInfoContext.Provider value={contextValue}>\n      {isMobile ? (\n        <Drawer.Root\n          key=\"drawer\"\n          direction={resolvedDirection}\n          open={open}\n          onOpenChange={handleOpenChange}\n          handleOnly={isSideDrawer}\n          noBodyStyles\n          repositionInputs={false}\n        >\n          {children}\n        </Drawer.Root>\n      ) : (\n        <Popover.Root key=\"popover\" open={open} onOpenChange={handleOpenChange}>\n          {children}\n        </Popover.Root>\n      )}\n    </WalletInfoContext.Provider>\n  );\n}\n\nexport const WalletInfo = Object.assign(WalletInfoRoot, {\n  Trigger: WalletInfoTrigger,\n  Content: WalletInfoContent,\n  Nav: WalletInfoNav,\n  Footer: WalletInfoFooter,\n});\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { DrawerDirection, Environment, Theme } from \"../../../types\";\nimport type { PreferredToken, TokenBalance, WalletInfoStyle } from \"../types\";\nimport type {\n  EstimateGasFn,\n  GetTransactionReceiptFn,\n  LpBalanceReaderFn,\n  PortfolioSection,\n  SendTransactionFn,\n  StakingRewardsReaderFn,\n} from \"../../wallet-portfolio/types\";\nimport type { SendAccount, SendAsset } from \"../../send\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport type {\n  BridgeApproveFn,\n  BridgeHistoryItem,\n  BridgeGetApprovalFn,\n  BridgeGetToTokensFn,\n  BridgeQuoteFn,\n  BridgeSubmitFn,\n  BridgeToken,\n} from \"../../bridge\";\nimport type { OnePopSummary } from \"../../onepop\";\n\nexport type WalletInfoView =\n  | \"wallet\"\n  | \"qr\"\n  | \"portfolio\"\n  | \"send\"\n  | \"bridge\"\n  | \"token-detail\"\n  | \"onepop\";\n\nexport interface WalletInfoContextValue {\n  env: Environment;\n  theme: Theme;\n  isMobile: boolean;\n  open: boolean;\n  drawerDirection: DrawerDirection;\n  modal: boolean;\n  showBalance: boolean;\n  showForgeToken: boolean;\n  showGameToken: boolean;\n  showQR: boolean;\n  showBridge: boolean;\n  qrLogoSrc?: string;\n  walletAddress: string;\n  accountName?: string;\n  sendAccounts?: SendAccount[];\n  profileImageUrl?: string;\n  connectorName?: string;\n  connectorIconUrl?: string;\n  preferredTokens: PreferredToken[];\n  close: () => void;\n  onSelectWallet?: () => void;\n  onCopyAddress?: (address: string, success: boolean) => void;\n  onBuy?: () => void | Promise<void>;\n  /**\n   * Buy 카드를 시각적으로 비활성 상태로 표시하고, 사용자가 클릭하면 이 메시지를\n   * 토스트로 띄운다. `onBuy`보다 우선. 예: on-ramp가 사용자 국가에서 차단된 경우.\n   * `onBuy`가 reject된 경우(예: allow/url API 실패)에도 동일 토스트 메시지로 재사용.\n   */\n  onBuyDisabledMessage?: string;\n  /**\n   * Bridge 카드를 시각적으로 비활성 상태로 표시하고, 클릭하면 이 메시지를\n   * 토스트로 띄운다. 브릿지 웹 이동보다 우선. 예: 연결된 지갑이 CROSSx\n   * 계열이 아니라 브릿지를 지원하지 않는 경우.\n   */\n  onBridgeDisabledMessage?: string;\n  /**\n   * Send 카드(및 토큰 행 탭으로 진입하는 전송 플로우)를 시각적으로 비활성\n   * 상태로 표시하고, 클릭하면 이 메시지를 토스트로 띄운다. `onSend` / 기본\n   * 토큰 선택 진입보다 우선. 예: 연결된 지갑이 CROSSx 계열이 아닌 경우.\n   */\n  onSendDisabledMessage?: string;\n  onDisconnect?: () => void;\n  disconnectLabel: string;\n  termsUrl?: string;\n  termsLabel: string;\n  privacyUrl?: string;\n  privacyLabel: string;\n  portalStyle?: WalletInfoStyle;\n  navContent: ReactNode | null;\n  navPosition: \"top\" | \"bottom\";\n  setNavContent: (content: ReactNode | null) => void;\n  setNavPosition: (position: \"top\" | \"bottom\") => void;\n  footerContent: ReactNode | null;\n  setFooterContent: (content: ReactNode | null) => void;\n  view: WalletInfoView;\n  setView: (view: WalletInfoView) => void;\n  selectedSendToken: SendAsset | null;\n  setSelectedSendToken: (token: SendAsset | null) => void;\n  /** 토큰 상세/이력 화면에서 보여줄 토큰. 토큰 행 탭 시 설정된다. */\n  selectedDetailToken: TokenBalance | null;\n  setSelectedDetailToken: (token: TokenBalance | null) => void;\n  showPortfolio: boolean;\n  /**\n   * 액션 행의 ONEpop 카드와 ONEpop 진입 화면 노출 여부. 기본 숨김(false).\n   * 화면 자체는 순수 UI라 아래 데이터/콜백이 없어도 렌더된다 —\n   * 액션 버튼은 ONEpop 서비스 딥링크가 기본 동작이다.\n   */\n  showOnePop: boolean;\n  /**\n   * ONEpop 화면의 상태(수령 대기 수/합계, replies 카운트, 최근 활동).\n   * dapp-ui는 `@nexus-cross/pop`(viem 필수)에 의존할 수 없어 직접 조회하지\n   * 않는다 — 호스트가 조회해 주입한다. 미주입 시 \"내역 없음\" 화면.\n   */\n  onePopSummary?: OnePopSummary;\n  /**\n   * ONEpop 뷰에 처음 진입할 때 1회 호출. 조회에 SIWE 서명이 필요해 지갑 팝업이\n   * 뜨므로, 주입측이 이 시점까지 조회를 미룰 수 있게 하는 트리거다.\n   */\n  onOnePopOpen?: () => void;\n  /** Send POP 카드 + \"Send your first POP!\" CTA. */\n  onOnePopSend?: () => void;\n  /** Claim POPs 카드 + 수령 대기 배너. */\n  onOnePopClaim?: () => void;\n  /** Activity 카드 + \"See all activity\" + replies 배너. */\n  onOnePopActivity?: () => void;\n  /**\n   * 활동 화면으로 나가는 경로에서 추가로 호출되는 알림 훅. 목적지 이동을\n   * 대체하지 않고, 답장 확인 처리(`repliesWaitingCount` 감소)에만 쓴다.\n   */\n  onOnePopRepliesSeen?: () => void;\n  /** ONEpop 워드마크 이미지. 미지정 시 내장 SVG. */\n  onePopGetTokenSlot?: React.ReactNode;\n  onePopLogoSrc?: string;\n  /** ONEpop 히어로 일러스트 이미지. 미지정 시 내장 SVG. */\n  onePopHeroSrc?: string;\n  portfolioTitle: string;\n  /**\n   * 노출할 포트폴리오 섹션 필터. 미지정 시 전체 노출, 배열 지정 시 포함된\n   * 섹션만 노출. `showPortfolio=true`일 때 `WalletPortfolioBody`로 릴레이된다.\n   */\n  portfolioSections?: PortfolioSection[];\n  showTotalAssets: boolean;\n  totalAssetsLabel: string;\n  /** Send 페이지의 일반 토큰 전송에 사용할 외부 트랜잭션 전송 함수. */\n  sendTransaction?: SendTransactionFn;\n  getTransactionReceipt?: GetTransactionReceiptFn;\n  /**\n   * Send 확인 단계에서 사용할 가스 추정 함수. SendPage로 그대로 전달된다.\n   */\n  estimateGas?: EstimateGasFn;\n  /**\n   * Terms/Privacy · 포트폴리오 섹션 등 외부 링크로 새 탭을 여는 모든 지점에서\n   * 호출되는 가로채기 콜백. 반환값으로 URL 변형 / 취소가 가능하며 async도 지원.\n   * 포트폴리오 영역은 `WalletPortfolioContext`로 그대로 릴레이된다.\n   */\n  onOutlink?: OnOutlink;\n  /**\n   * game-swap LP 섹션이 사용자별 LP 잔고/지분율을 온체인에서 보강하기 위한\n   * 주입형 reader. `showPortfolio=true`일 때 `WalletPortfolioBody`로 릴레이된다.\n   */\n  lpBalanceReader?: LpBalanceReaderFn;\n  /**\n   * CROSS Staking 섹션이 온체인 `earned()` / `balanceOf()`를 보강하기 위한\n   * 주입형 reader. `showPortfolio=true`일 때 `WalletPortfolioBody`로 릴레이된다.\n   */\n  stakingRewardsReader?: StakingRewardsReaderFn;\n  bridgeTokens?: BridgeToken[];\n  bridgeHistory?: BridgeHistoryItem[];\n  getBridgeQuote?: BridgeQuoteFn;\n  getBridgeToTokens?: BridgeGetToTokensFn;\n  getBridgeApproval?: BridgeGetApprovalFn;\n  approveBridge?: BridgeApproveFn;\n  submitBridge?: BridgeSubmitFn;\n  /**\n   * 상단 QR 버튼 / 기본 액션 row의 Receive / Send 콜백. (Buy는 위\n   * onBuy/onBuyDisabledMessage로 정의됨.)\n   */\n  onReceive?: () => void;\n  /**\n   * @deprecated Bridge 버튼은 항상 apps.json(gametokenBridge) 웹으로\n   * 이동한다. 이 콜백은 더 이상 호출되지 않는다.\n   */\n  onBridge?: () => void;\n  onSend?: () => void;\n}\n\nexport const WalletInfoContext = createContext<WalletInfoContextValue | null>(\n  null,\n);\n\nexport function useWalletInfoContext() {\n  const ctx = useContext(WalletInfoContext);\n  if (!ctx) {\n    throw new Error(\n      \"WalletInfo compound components must be used within <WalletInfo>\",\n    );\n  }\n  return ctx;\n}\n","import { WALLET_LABELS } from \"@nexus-cross/crossx-design-system\";\nimport type { CSSProperties } from \"react\";\n\n/** CAIP-2 chain identifier (e.g. \"eip155:612044\") */\nexport type ChainId = `eip155:${number}`;\n\nexport enum ConnectorId {\n  CROSSx = \"crossx\",\n  MetaMask = \"io.metamask\",\n  Binance = \"com.binance.wallet\",\n}\n\nexport interface ConnectorMeta {\n  name: string;\n  iconUrl: string;\n}\n\nexport const CONNECTOR_REGISTRY: Record<ConnectorId, ConnectorMeta> = {\n  [ConnectorId.CROSSx]: {\n    name: WALLET_LABELS.default,\n    iconUrl: \"https://contents.crosstoken.io/wallet/token/images/tCROSS.svg\",\n  },\n  [ConnectorId.MetaMask]: {\n    name: \"MetaMask\",\n    iconUrl: \"https://upload.wikimedia.org/wikipedia/commons/3/36/MetaMask_Fox.svg\",\n  },\n  [ConnectorId.Binance]: {\n    name: \"Binance Wallet\",\n    iconUrl: \"https://public.bnbstatic.com/static/images/common/favicon.ico\",\n  },\n};\n\nexport interface PreferredToken {\n  chainId: ChainId;\n  address: string;\n}\n\nexport interface WalletInfoStyle extends CSSProperties {\n  \"--wi-primary\"?: string;\n  \"--wi-secondary\"?: string;\n  \"--wi-surface-bg\"?: string;\n  \"--wi-surface-default\"?: string;\n  \"--wi-surface-subtle\"?: string;\n  \"--wi-border-default\"?: string;\n  \"--wi-border-subtle\"?: string;\n  \"--wi-texticon-primary\"?: string;\n  \"--wi-texticon-secondary\"?: string;\n  \"--wi-texticon-tertiary\"?: string;\n}\n\nexport interface TokenBalance {\n  blockNumber: number;\n  name: string;\n  symbol: string;\n  chainId: number;\n  address: string;\n  quantity: {\n    decimals: number;\n    numeric: string;\n  };\n  icon_url: string;\n}\n\nexport interface TokenBalanceResponse {\n  code: number;\n  message: string;\n  data: TokenBalance[];\n}\n\nexport interface TokenStats {\n  chain_id: number;\n  address: string;\n  price: string;\n  percent_change_24h: string;\n  /** 시장 유통량. `/v1/public/token/stats`가 제공(문자열). 상세화면 Total Supply 표기용. */\n  circulating_supply?: string;\n  market_cap?: string;\n  volume_24h?: string;\n}\n\nexport interface TokenStatsResponse {\n  code: number;\n  message: string;\n  data: TokenStats[];\n}\n\nexport interface TokenInfo {\n  chain_id: number;\n  address: string;\n  category: string;\n  /**\n   * `/v1/public/token/info` 카탈로그에 함께 담겨 오는 메타데이터. 토큰 상세화면의\n   * Social Media 링크 및 컨트랙트 배포 여부 판별에 사용한다. 응답에 없을 수 있어\n   * 모두 optional로 둔다.\n   */\n  name?: string;\n  symbol?: string;\n  decimals?: number;\n  image?: string;\n  deployed?: boolean;\n  info_url?: string | null;\n  x_url?: string | null;\n  telegram_url?: string | null;\n  medium_url?: string | null;\n  discord_url?: string | null;\n}\n\nexport interface TokenInfoResponse {\n  code: number;\n  message: string;\n  data: TokenInfo[];\n}\n\n/** `/v1/public/token/historical` 단건. Receive/Send는 from/to를 지갑주소와 비교해 판별한다. */\nexport interface TokenHistoryItem {\n  amount: string;\n  block_number: number;\n  chain_id: number;\n  from: string;\n  from_alias: string;\n  log_index: number;\n  method: string;\n  method_alias: string;\n  nonce: number;\n  removed: boolean;\n  timestamp: number;\n  to: string;\n  to_alias: string;\n  token: string;\n  tx: string;\n  tx_index: number;\n  gas_price: number;\n  gas_used: number;\n  max_fee_per_gas: number;\n  status: \"success\" | \"fail\";\n}\n\nexport interface TokenHistoryResponse {\n  code: number;\n  message: string;\n  data: TokenHistoryItem[];\n}\n\n/** `/v1/public/token/price/historical`가 받는 interval 값(표시 라벨과 다름). */\nexport type TokenPriceInterval = \"10m\" | \"1h\" | \"1d\" | \"7d\" | \"30d\" | \"1y\";\n\n/** 가격 히스토리 단건. `updated_at`은 unix 초. */\nexport interface TokenPricePoint {\n  price: string;\n  updated_at: number;\n}\n\nexport interface TokenPriceHistoryResponse {\n  code: number;\n  message: string;\n  data: TokenPricePoint[];\n}\n\nexport interface ForgeHolding {\n  token: string;\n  token_name: string;\n  token_symbol: string;\n  balance: string;\n  current_price: string;\n  value: string;\n  average_cost_usd: number;\n  current_value_usd: number;\n  unrealized_pnl_usd: number;\n  unrealized_pnl_cross: number;\n  pnl_percentage: number;\n}\n\nexport interface ForgeHoldingsResponse {\n  success: boolean;\n  data: {\n    items: ForgeHolding[];\n  };\n}\n\nexport interface ForgeTokenDetail {\n  address: string;\n  name: string;\n  symbol: string;\n  image_url: string;\n}\n\nexport interface ForgeTokenDetailResponse {\n  success: boolean;\n  data: {\n    token: ForgeTokenDetail;\n  };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../../lib/utils\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport { BackIcon, CloseIcon } from \"../../wallet-info/components/icons\";\nimport type { EstimateGasFn, GetTransactionReceiptFn, SendTransactionFn } from \"../../wallet-portfolio/types\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport type { Environment, Theme } from \"../../../types\";\nimport {\n  useSendFlow,\n  type SendFailedInfo,\n  type SendSubmittedInfo,\n} from \"../model/useSendFlow\";\nimport type { SendAccount, SendAsset } from \"../types\";\nimport { parseTokenAmount, isAddress } from \"../utils\";\nimport { SendAmount } from \"./steps/SendAmount\";\nimport { SendRecipient } from \"./steps/SendRecipient\";\nimport { SendResult } from \"./steps/SendResult\";\nimport { SendReview } from \"./steps/SendReview\";\nimport { SendTokenSelect } from \"./steps/SendTokenSelect\";\nimport \"../styles/send-flow.css\";\n\nexport interface SendFlowProps {\n  env?: Environment;\n  theme?: Theme;\n  walletAddress: string;\n  accountName?: string;\n  accounts?: SendAccount[];\n  /** First step shown when the flow opens. Defaults to `recipient`. */\n  initialStep?: \"token\" | \"recipient\";\n  token: SendAsset;\n  tokens?: SendAsset[];\n  onTokenChange?: (token: SendAsset) => void;\n  sendTransaction?: SendTransactionFn;\n  getTransactionReceipt?: GetTransactionReceiptFn;\n  estimateGas?: EstimateGasFn;\n  getTokenPriceUsd?: (token: SendAsset) => number | undefined;\n  onSubmitted?: (info: SendSubmittedInfo) => void;\n  onSuccess?: (txHash: `0x${string}`) => void;\n  onFailed?: (info: SendFailedInfo) => void;\n  /** Close the whole surface (X). */\n  onClose?: () => void;\n  /** Back out of the first step (return to the wallet view). */\n  onBackToWallet?: () => void;\n  /** Finished after a successful send. */\n  onConfirmSuccess?: (txHash?: `0x${string}`) => void;\n  onOutlink?: OnOutlink;\n  className?: string;\n}\n\nconst TITLES: Record<string, string> = {\n  token: \"Send\",\n  recipient: \"Send\",\n  amount: \"Send\",\n  review: \"Sign Transaction\",\n  result: \"\",\n};\n\nexport function SendFlow({\n  onClose,\n  onBackToWallet,\n  onConfirmSuccess,\n  onSuccess,\n  className,\n  ...rest\n}: SendFlowProps) {\n  const handleSuccess = React.useCallback(\n    (txHash: `0x${string}`) => {\n      onSuccess?.(txHash);\n      onConfirmSuccess?.(txHash);\n    },\n    [onConfirmSuccess, onSuccess],\n  );\n  const successHandler = onSuccess || onConfirmSuccess ? handleSuccess : undefined;\n  const flow = useSendFlow({\n    ...rest,\n    onSuccess: successHandler,\n    onExit: onBackToWallet,\n  });\n  const { step, status } = flow;\n\n  const recipientValid = isAddress(flow.recipient.trim());\n  const amountParsed = parseTokenAmount(flow.amount, flow.token.quantity.decimals);\n  const amountValid =\n    \"value\" in amountParsed && amountParsed.value <= flow.balanceRaw;\n  const sending = status === \"submitting\" || status === \"confirming\";\n\n  const showBack = step !== \"result\";\n  const title = TITLES[step] ?? \"\";\n\n  return (\n    <TrackingBoundary feature=\"send\">\n    <div className={cn(\"sf-root\", className)} data-step={step}>\n      <header className=\"sf-header\">\n        {showBack ? (\n          <button\n            type=\"button\"\n            className=\"sf-header-btn\"\n            onClick={flow.back}\n            aria-label=\"Back\"\n            data-track=\"back\"\n          >\n            <BackIcon size={24} />\n          </button>\n        ) : (\n          <span className=\"sf-header-btn\" aria-hidden />\n        )}\n        <span className=\"sf-header-title\">{title}</span>\n        <button\n          type=\"button\"\n          className=\"sf-header-btn\"\n          onClick={onClose}\n          aria-label=\"Close\"\n          data-track=\"close\"\n        >\n          <CloseIcon size={24} />\n        </button>\n      </header>\n\n      {step === \"token\" && <SendTokenSelect flow={flow} />}\n      {step === \"recipient\" && <SendRecipient flow={flow} />}\n      {step === \"amount\" && <SendAmount flow={flow} />}\n      {step === \"review\" && <SendReview flow={flow} />}\n      {step === \"result\" && <SendResult flow={flow} />}\n\n      <footer className=\"sf-footer\" data-step={step}>\n        {step === \"recipient\" && (\n          <button\n            type=\"button\"\n            className=\"sf-btn sf-btn--primary\"\n            onClick={flow.submitRecipient}\n            data-disabled={!recipientValid || undefined}\n            aria-disabled={!recipientValid}\n            data-track=\"recipient_next\"\n          >\n            Next\n          </button>\n        )}\n        {step === \"amount\" && (\n          <button\n            type=\"button\"\n            className=\"sf-btn sf-btn--primary\"\n            onClick={flow.submitAmount}\n            data-disabled={!amountValid || undefined}\n            aria-disabled={!amountValid}\n            data-track=\"amount_next\"\n          >\n            Next\n          </button>\n        )}\n        {step === \"review\" && (\n          <div className=\"sf-footer-row\">\n            <button\n              type=\"button\"\n              className=\"sf-btn sf-btn--secondary\"\n              onClick={flow.back}\n              disabled={sending}\n              data-track=\"review_cancel\"\n            >\n              Cancel\n            </button>\n            <button\n              type=\"button\"\n              className=\"sf-btn sf-btn--primary\"\n              onClick={flow.confirmSend}\n              disabled={sending || !flow.canSend || flow.insufficientGas}\n              data-track=\"review_send\"\n            >\n              {sending ? \"Sending...\" : \"Send\"}\n            </button>\n          </div>\n        )}\n        {step === \"result\" && status === \"success\" && (\n          <button\n            type=\"button\"\n            className=\"sf-btn sf-btn--primary\"\n            onClick={() => onConfirmSuccess?.(flow.txHash || undefined)}\n            data-track=\"result_ok\"\n          >\n            All done\n          </button>\n        )}\n        {step === \"result\" && status === \"error\" && (\n          <button\n            type=\"button\"\n            className=\"sf-btn sf-btn--primary\"\n            onClick={flow.retry}\n            data-track=\"result_retry\"\n          >\n            Try Again\n          </button>\n        )}\n      </footer>\n    </div>\n    </TrackingBoundary>\n  );\n}\n","interface IconProps {\n  size?: number;\n  className?: string;\n}\n\nexport function WalletIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M12.6667 4.66667V2.66667C12.6667 2.48986 12.5964 2.32029 12.4714 2.19526C12.3464 2.07024 12.1768 2 12 2H3.33333C2.97971 2 2.64057 2.14048 2.39052 2.39052C2.14048 2.64057 2 2.97971 2 3.33333C2 3.68696 2.14048 4.02609 2.39052 4.27614C2.64057 4.52619 2.97971 4.66667 3.33333 4.66667H13.3333C13.5101 4.66667 13.6797 4.7369 13.8047 4.86193C13.9298 4.98695 14 5.15652 14 5.33333V8M14 8H12C11.6464 8 11.3072 8.14048 11.0572 8.39052C10.8071 8.64057 10.6667 8.97971 10.6667 9.33333C10.6667 9.68696 10.8071 10.0261 11.0572 10.2761C11.3072 10.5262 11.6464 10.6667 12 10.6667H14C14.1768 10.6667 14.3464 10.5964 14.4714 10.4714C14.5964 10.3464 14.6667 10.1768 14.6667 10V8.66667C14.6667 8.48986 14.5964 8.32029 14.4714 8.19526C14.3464 8.07024 14.1768 8 14 8Z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.33333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M2 3.33325V12.6666C2 13.0202 2.14048 13.3593 2.39052 13.6094C2.64057 13.8594 2.97971 13.9999 3.33333 13.9999H13.3333C13.5101 13.9999 13.6797 13.9297 13.8047 13.8047C13.9298 13.6796 14 13.5101 14 13.3333V10.6666\"\n        stroke=\"currentColor\"\n        strokeWidth={1.33333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function CopyIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"4 4 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M9.40284 17.0647H4.85156V4.84656H17.0697V9.39783H15.4407V6.47565H6.48066V15.4356H9.40284V17.0647ZM21.5192 12.5644H12.5592V21.5244H21.5192V12.5644ZM23.1483 10.9353V23.1535H10.9301V10.9353H23.1483Z\"\n        fill=\"currentColor\"\n      />\n    </svg>\n  );\n}\n\nexport function CheckIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 14 14\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M11.6667 3.5L5.25 9.91667L2.33334 7\"\n        stroke=\"currentColor\"\n        strokeWidth={1.16667}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function CloseIcon({ size = 24, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M18 6L6 18M6 6l12 12\"\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function DisconnectIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M7.5 17.5H4.167A1.667 1.667 0 012.5 15.833V4.167A1.667 1.667 0 014.167 2.5H7.5\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M13.333 14.167L17.5 10l-4.167-4.167M17.5 10H7.5\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function QRIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      className={className}\n    >\n      <path\n        d=\"M7.91305 10.8327C8.60341 10.8327 9.16305 11.3924 9.16305 12.0827V16.2458C9.16305 16.9361 8.60341 17.4958 7.91305 17.4958H3.75C3.05964 17.4958 2.5 16.9361 2.5 16.2458L2.5 12.0827C2.5 11.3924 3.05964 10.8327 3.75 10.8327H7.91305ZM14.1685 17.4958H12.5027V15.83H14.1685V17.4958ZM17.5 17.4958H15.8342V15.83H17.5V17.4958ZM4.16576 12.4985V15.83H7.49729V12.4985H4.16576ZM12.5027 15.83H10.8369V14.1642H12.5027V15.83ZM15.8342 15.83H14.1685V14.1642H15.8342V15.83ZM14.1685 14.1642H12.5027V12.4985H14.1685V14.1642ZM17.5 14.1642H15.8342V12.4985H17.5V14.1642ZM12.5027 12.4985H10.8369V10.8327H12.5027V12.4985ZM15.8342 12.4985H14.1685V10.8327H15.8342V12.4985ZM7.91305 2.50391C8.60341 2.50391 9.16305 3.06355 9.16305 3.75391V7.91696C9.16305 8.60731 8.60341 9.16696 7.91305 9.16696H3.75C3.05964 9.16696 2.5 8.60731 2.5 7.91696L2.5 3.75391C2.5 3.06355 3.05964 2.50391 3.75 2.50391L7.91305 2.50391ZM16.2419 2.50391C16.9322 2.50391 17.4919 3.06355 17.4919 3.75391V7.91696C17.4919 8.60731 16.9322 9.16696 16.2419 9.16696H12.0788C11.3885 9.16696 10.8288 8.60731 10.8288 7.91696V3.75391C10.8288 3.06355 11.3885 2.50391 12.0788 2.50391L16.2419 2.50391ZM4.16576 4.16967L4.16576 7.50119L7.49729 7.50119L7.49729 4.16967L4.16576 4.16967ZM12.4946 4.16967V7.50119H15.8261V4.16967H12.4946Z\"\n        fill=\"currentColor\"\n        fillOpacity={1}\n      />\n    </svg>\n  );\n}\n\nexport function BackIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M12.5 15L7.5 10L12.5 5\"\n        stroke=\"currentColor\"\n        strokeWidth={1.67}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ChevronRightIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M8 5L13 10L8 15\"\n        stroke=\"currentColor\"\n        strokeWidth={1.67}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ChevronDownIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M5 8L10 13L15 8\"\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function EyeOpenIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M1.667 10S4.167 4.167 10 4.167 18.333 10 18.333 10 15.833 15.833 10 15.833 1.667 10 1.667 10z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M10 12.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function EyeClosedIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M14.95 14.95A8.283 8.283 0 0110 16.667c-5.833 0-8.333-5.833-8.333-5.833a15.238 15.238 0 014.183-4.884m3.267-1.55A7.55 7.55 0 0110 4.167c5.833 0 8.333 5.833 8.333 5.833a15.28 15.28 0 01-1.783 2.633M11.767 11.767a2.5 2.5 0 11-3.534-3.534\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M1.667 1.667l16.666 16.666\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n/* ─── 액션 행 아이콘. 모두 20x20 base, currentColor stroke. ─── */\n\nexport function ReceiveActionIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M17.5 13.3333H15C14.558 13.3333 14.134 13.5089 13.8215 13.8215C13.5089 14.134 13.3333 14.558 13.3333 15V17.5M17.5 17.5V17.5083M10 5.83333V8.33333C10 8.77536 9.8244 9.19928 9.51184 9.51184C9.19928 9.8244 8.77536 10 8.33333 10H5.83333M2.5 10H2.50833M10 2.5H10.0083M10 13.3333V13.3417M13.3333 10H14.1667M17.5 10V10.0083M10 17.5V16.6667M3.33333 2.5H5.83333C6.29357 2.5 6.66667 2.8731 6.66667 3.33333V5.83333C6.66667 6.29357 6.29357 6.66667 5.83333 6.66667H3.33333C2.8731 6.66667 2.5 6.29357 2.5 5.83333V3.33333C2.5 2.8731 2.8731 2.5 3.33333 2.5ZM14.1667 2.5H16.6667C17.1269 2.5 17.5 2.8731 17.5 3.33333V5.83333C17.5 6.29357 17.1269 6.66667 16.6667 6.66667H14.1667C13.7064 6.66667 13.3333 6.29357 13.3333 5.83333V3.33333C13.3333 2.8731 13.7064 2.5 14.1667 2.5ZM3.33333 13.3333H5.83333C6.29357 13.3333 6.66667 13.7064 6.66667 14.1667V16.6667C6.66667 17.1269 6.29357 17.5 5.83333 17.5H3.33333C2.8731 17.5 2.5 17.1269 2.5 16.6667V14.1667C2.5 13.7064 2.8731 13.3333 3.33333 13.3333Z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n// ONEwallet brand mark (pink→orange gradient) for the QR-code centre.\n// Rendered as an isolated <img> data-URI so the SVG's mask/filter/gradient\n// ids can't collide with other inline instances of the logo.\nconst QR_LOGO_SVG = `<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 100 100\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_72837_24069)\"><mask id=\"mask0_72837_24069\" style=\"mask-type:luminance\" maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width=\"100\" height=\"100\"><path d=\"M50 0C10.5289 0 0 10.5289 0 50C0 89.4711 10.5289 100 50 100C89.4711 100 100 89.4711 100 50C100 10.5289 89.4711 0 50 0Z\" fill=\"white\"/></mask><g mask=\"url(#mask0_72837_24069)\"><path d=\"M99.9501 0.0499268H0V100H99.9501V0.0499268Z\" fill=\"url(#paint0_linear_72837_24069)\"/><path d=\"M34.9302 100H99.9751V19.0619L34.9302 60.7286V100Z\" fill=\"url(#paint1_linear_72837_24069)\"/><path d=\"M34.9301 100H0V23.8024L34.9301 60.7285V100Z\" fill=\"url(#paint2_linear_72837_24069)\"/></g><g filter=\"url(#filter0_d_72837_24069)\"><path d=\"M49.9503 21.4821L31.9862 31.8613C28.8176 33.6827 27.7198 37.7745 29.5411 40.9432C31.3625 44.1118 35.4543 45.2096 38.623 43.3883L53.8176 34.6058L46.582 42.3653V72.8044C46.582 76.4721 49.576 79.4661 53.2437 79.4661C56.9114 79.4661 59.9054 76.4721 59.9054 72.8044V27.2455C59.9054 25.9731 59.5062 24.7256 59.0072 23.9022C57.1359 20.7585 53.094 19.6358 49.9254 21.4571L49.9503 21.4821Z\" fill=\"white\"/></g></g><defs><filter id=\"filter0_d_72837_24069\" x=\"7.4286\" y=\"-0.650014\" width=\"87.831\" height=\"115.47\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\"><feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/><feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\" result=\"hardAlpha\"/><feOffset dx=\"7.06088\" dy=\"7.06088\"/><feGaussianBlur stdDeviation=\"14.1467\"/><feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.831373 0 0 0 0 0.113725 0 0 0 0 0.356863 0 0 0 0.3 0\"/><feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow_72837_24069\"/><feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect1_dropShadow_72837_24069\" result=\"shape\"/></filter><linearGradient id=\"paint0_linear_72837_24069\" x1=\"-11.5768\" y1=\"-12.001\" x2=\"106.562\" y2=\"106.986\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#F3752D\"/><stop offset=\"1\" stop-color=\"#EC1272\"/></linearGradient><linearGradient id=\"paint1_linear_72837_24069\" x1=\"24.9501\" y1=\"23.004\" x2=\"73.6278\" y2=\"64.8453\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"white\" stop-opacity=\"0.5\"/><stop offset=\"1\" stop-color=\"white\" stop-opacity=\"0\"/></linearGradient><linearGradient id=\"paint2_linear_72837_24069\" x1=\"62.3503\" y1=\"3.46804\" x2=\"16.3423\" y2=\"63.3732\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"white\" stop-opacity=\"0.5\"/><stop offset=\"1\" stop-color=\"white\" stop-opacity=\"0\"/></linearGradient><clipPath id=\"clip0_72837_24069\"><rect width=\"100\" height=\"100\" fill=\"white\"/></clipPath></defs></svg>`;\n\nconst QR_LOGO_ICON = `data:image/svg+xml,${encodeURIComponent(QR_LOGO_SVG)}`;\n\nexport function QRLogoIcon({ size = 42, className }: IconProps) {\n  return (\n    <img\n      src={QR_LOGO_ICON}\n      width={size}\n      height={size}\n      alt=\"\"\n      className={className}\n      style={{ display: \"block\" }}\n    />\n  );\n}\n\nexport function SendActionIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M9.19133 10.8077C9.04905 10.6657 8.87947 10.554 8.69283 10.4792L2.78881 8.11167C2.71832 8.08338 2.65816 8.03421 2.61642 7.97075C2.57468 7.90729 2.55335 7.83258 2.5553 7.75665C2.55724 7.68072 2.58237 7.6072 2.6273 7.54596C2.67224 7.48472 2.73483 7.4387 2.80668 7.41406L16.9525 2.5747C17.0185 2.55087 17.0899 2.54632 17.1583 2.56159C17.2268 2.57685 17.2895 2.6113 17.3391 2.66089C17.3887 2.71049 17.4231 2.77319 17.4384 2.84165C17.4536 2.9101 17.4491 2.9815 17.4253 3.04746L12.5859 17.1933C12.5613 17.2651 12.5152 17.3277 12.454 17.3727C12.3928 17.4176 12.3192 17.4427 12.2433 17.4447C12.1674 17.4466 12.0927 17.4253 12.0292 17.3835C11.9658 17.3418 11.9166 17.2817 11.8883 17.2112L9.52073 11.3056C9.44567 11.1191 9.33362 10.9498 9.19133 10.8077ZM9.19133 10.8077L17.3367 2.66404\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n/**\n * 총자산 행 우측의 Send 버튼 아이콘 (Figma `mynaui:send-solid`).\n * 액션 행의 outline `SendActionIcon`과 달리 채워진 형태이며, 색은 브랜드\n * 그라디언트를 SVG 안에서 직접 칠한다.\n */\nexport function SendSolidIcon({ size = 24, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <defs>\n        <linearGradient\n          id=\"wi-send-solid-grad\"\n          x1=\"3\"\n          y1=\"21\"\n          x2=\"21\"\n          y2=\"3\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"var(--wi-send-solid-from, #f62e61)\" />\n          <stop offset=\"1\" stopColor=\"var(--wi-send-solid-to, #f7564a)\" />\n        </linearGradient>\n      </defs>\n      <path\n        d=\"M20.98 3.02a1.2 1.2 0 0 0-1.28-.28L3.6 8.9a1.2 1.2 0 0 0 .07 2.26l6.06 1.9a.6.6 0 0 1 .39.4l1.9 6.06a1.2 1.2 0 0 0 2.26.07l6.16-16.1a1.2 1.2 0 0 0-.46-1.47Z\"\n        fill=\"url(#wi-send-solid-grad)\"\n      />\n    </svg>\n  );\n}\n\nexport function PortfolioActionIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M10.8333 14.1667V7.5M15 14.1667V4.16667M2.5 2.5V15.8333C2.5 16.2754 2.67559 16.6993 2.98816 17.0118C3.30072 17.3244 3.72464 17.5 4.16667 17.5H17.5M6.66667 14.1667V11.6667\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function BuyActionIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M6.6665 9.99984H13.3332M9.99984 6.6665V13.3332M18.3332 9.99984C18.3332 14.6022 14.6022 18.3332 9.99984 18.3332C5.39746 18.3332 1.6665 14.6022 1.6665 9.99984C1.6665 5.39746 5.39746 1.6665 9.99984 1.6665C14.6022 1.6665 18.3332 5.39746 18.3332 9.99984Z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function BridgeActionIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M6.667 2.5 3.333 5.833 6.667 9.167M3.333 5.833h13.334M13.333 17.5l3.334-3.333-3.334-3.334M16.667 14.167H3.333\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ToastCheckIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <circle cx=\"10\" cy=\"10\" r=\"10\" fill=\"currentColor\" />\n      <path\n        d=\"M6 10.2L8.6 12.8L14 7.2\"\n        stroke=\"var(--ds-accent-primary-on, #ffffff)\"\n        strokeWidth={1.8}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n","\"use client\";\n\n/**\n * Send feature — flow state machine (React hook).\n *\n * Owns the multi-step Send UX (token → recipient → amount → review → result)\n * and the transaction lifecycle, delegating all logic to {@link sendUseCases}\n * via a {@link SendRepository}. Screens are pure presenters of what this\n * returns; they hold no business logic.\n */\n\nimport * as React from \"react\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport { useChainDisplay } from \"../../../lib/remote-config\";\nimport {\n  normalizeFailureReason,\n  trackDappUiFunnel,\n  type TrackDappUiFunnelOptions,\n} from \"../../../lib/sentry/index\";\nimport { formatWei } from \"../../../lib/format\";\nimport type {\n  EstimateGasFn,\n  GasEstimate,\n  GetTransactionReceiptFn,\n  SendTransactionFn,\n} from \"../../wallet-portfolio/types\";\nimport { useTransactionExplorerUrl } from \"../queries/use-chain-info\";\nimport type { Environment } from \"../../../types\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport type { SendAccount, SendAsset } from \"../types\";\nimport {\n  getRecentSendAddresses,\n  isNativeSendAsset,\n  saveRecentSendAddress,\n} from \"../utils\";\nimport { getEstTxFeeWei, isBscLegacyChainId } from \"./sendFormat\";\nimport { formatSendError } from \"../utils/sendErrorMessage\";\nimport { createSendRepository } from \"./SendRepository\";\nimport {\n  buildSendTxArgs,\n  executeSend,\n  hasFieldErrors,\n  type SendFieldErrors,\n  validateSend,\n} from \"./sendUseCases\";\n\nexport type SendStep = \"token\" | \"recipient\" | \"amount\" | \"review\" | \"result\";\nexport type SendStatus =\n  | \"idle\"\n  | \"submitting\"\n  | \"confirming\"\n  | \"success\"\n  | \"error\";\n\nexport interface SendSubmittedInfo {\n  txHash: `0x${string}`;\n  chainId: number;\n  amount: string;\n  tokenSymbol: string;\n  recipient: `0x${string}`;\n}\n\nexport interface SendFailedInfo {\n  txHash?: `0x${string}`;\n  chainId: number;\n  message: string;\n}\n\nexport interface UseSendFlowOptions {\n  env?: Environment;\n  walletAddress: string;\n  accountName?: string;\n  accounts?: SendAccount[];\n  initialStep?: \"token\" | \"recipient\";\n  /** Token preselected by the host (wallet-info token click). */\n  token: SendAsset;\n  tokens?: SendAsset[];\n  onTokenChange?: (token: SendAsset) => void;\n  sendTransaction?: SendTransactionFn;\n  getTransactionReceipt?: GetTransactionReceiptFn;\n  estimateGas?: EstimateGasFn;\n  /** Optional USD price lookup for a token (key = chainId:address). */\n  getTokenPriceUsd?: (token: SendAsset) => number | undefined;\n  onSubmitted?: (info: SendSubmittedInfo) => void;\n  onSuccess?: (txHash: `0x${string}`) => void;\n  onFailed?: (info: SendFailedInfo) => void;\n  /** Called when the user leaves the first step (back out of Send). */\n  onExit?: () => void;\n  onOutlink?: OnOutlink;\n}\n\nconst STEP_ORDER: SendStep[] = [\"token\", \"recipient\", \"amount\", \"review\"];\n\nexport function useSendFlow(options: UseSendFlowOptions) {\n  const {\n    env,\n    walletAddress,\n    accountName,\n    accounts = [],\n    initialStep = \"recipient\",\n    token: initialToken,\n    tokens = [],\n    onTokenChange,\n    sendTransaction,\n    getTransactionReceipt,\n    estimateGas,\n    getTokenPriceUsd,\n    onSubmitted,\n    onSuccess,\n    onFailed,\n    onExit,\n    onOutlink,\n  } = options;\n\n  const repository = React.useMemo(\n    () => createSendRepository({ sendTransaction, getTransactionReceipt, estimateGas }),\n    [sendTransaction, getTransactionReceipt, estimateGas],\n  );\n\n  const [token, setToken] = React.useState<SendAsset>(initialToken);\n  // Re-sync when the host swaps the preselected token while mounted.\n  React.useEffect(() => {\n    setToken(initialToken);\n  }, [initialToken.address, initialToken.chainId]);\n\n  const [step, setStep] = React.useState<SendStep>(initialStep);\n  const [recipient, setRecipientState] = React.useState(\"\");\n  const [amount, setAmountState] = React.useState(\"\");\n  const [fieldErrors, setFieldErrors] = React.useState<SendFieldErrors>({});\n  const [status, setStatus] = React.useState<SendStatus>(\"idle\");\n  const [txHash, setTxHash] = React.useState<`0x${string}` | \"\">(\"\");\n  const [error, setError] = React.useState(\"\");\n  const [gasEstimate, setGasEstimate] = React.useState<GasEstimate | null>(null);\n  const [gasLoading, setGasLoading] = React.useState(false);\n  const [gasError, setGasError] = React.useState(\"\");\n  const gasReqIdRef = React.useRef(0);\n  const [reviewRawAmount, setReviewRawAmount] = React.useState<bigint | null>(null);\n  const [reviewRecipient, setReviewRecipient] = React.useState<`0x${string}` | \"\">(\"\");\n  const [recentAddresses, setRecentAddresses] = React.useState(() =>\n    getRecentSendAddresses(walletAddress),\n  );\n\n  const funnelFiredRef = React.useRef(new Set<string>());\n  const trackFunnelOnce = React.useCallback(\n    (funnelStep: string, funnelOptions?: TrackDappUiFunnelOptions) => {\n      if (funnelFiredRef.current.has(funnelStep)) return;\n      funnelFiredRef.current.add(funnelStep);\n      trackDappUiFunnel(\"send\", funnelStep, funnelOptions);\n    },\n    [],\n  );\n\n  React.useEffect(() => {\n    trackFunnelOnce(\"opened\");\n    if (initialStep !== \"token\") {\n      trackFunnelOnce(\"token_selected\", { tags: { item_id: initialToken.symbol } });\n    }\n  }, [trackFunnelOnce, initialStep, initialToken.symbol]);\n\n  const resolvedEnv = React.useMemo(() => resolveEnvironment(env), [env]);\n  const { transactionUrl, chainInfo } = useTransactionExplorerUrl(\n    resolvedEnv,\n    token.chainId,\n    txHash || undefined,\n    true,\n  );\n  // S3(chains.json) 표시값이 최우선 override, 없으면 백엔드 chainInfo, 그다음 폴백.\n  // nativeSymbol 은 표시·에러메시지 전용(비교/매칭 로직엔 사용 안 함)이라 override 안전.\n  const chainDisplay = useChainDisplay(token.chainId, resolvedEnv);\n  const nativeSymbol =\n    chainDisplay.nativeSymbol ?? chainInfo?.currency_symbol ?? \"\";\n  const networkName =\n    chainDisplay.name ?? chainInfo?.name ?? `Chain ID ${token.chainId}`;\n\n  // Error-message context for gas estimation / send. Held in a ref so the\n  // gas-estimate effect does NOT re-run (and cancel its in-flight request)\n  // when chainInfo resolves and flips nativeSymbol / networkName — that churn\n  // previously left the estimate stuck on its loading state forever.\n  const errorCtxRef = React.useRef({\n    nativeSymbol: nativeSymbol || undefined,\n    networkName,\n    tokenSymbol: token.symbol,\n  });\n  errorCtxRef.current = {\n    nativeSymbol: nativeSymbol || undefined,\n    networkName,\n    tokenSymbol: token.symbol,\n  };\n\n  const balanceRaw = React.useMemo(\n    () => BigInt(token.quantity.numeric || \"0\"),\n    [token.quantity.numeric],\n  );\n  const balanceText = React.useMemo(\n    () => formatWei(token.quantity.numeric, token.quantity.decimals),\n    [token.quantity.numeric, token.quantity.decimals],\n  );\n  const priceUsd = getTokenPriceUsd?.(token);\n  const amountUsd = React.useMemo(() => {\n    if (priceUsd === undefined) return undefined;\n    const n = parseFloat(amount.replace(/,/g, \"\"));\n    return Number.isFinite(n) ? n * priceUsd : 0;\n  }, [amount, priceUsd]);\n\n  // Reset transient state when the token changes.\n  React.useEffect(() => {\n    setAmountState(\"\");\n    setFieldErrors({});\n  }, [token.address, token.chainId]);\n\n  // 입력(토큰/수신자/금액)이 바뀌면 진행 중이던 가스 추정을 무효화한다\n  // (develop SendPage 와 동일). reqId 를 올려 in-flight 결과가 다음 화면에\n  // stale 하게 안착하는 것을 막는다. cleanup 대신 이 무효화로만 staleness 를 관리.\n  React.useEffect(() => {\n    gasReqIdRef.current += 1;\n    setGasEstimate(null);\n    setGasError(\"\");\n    setGasLoading(false);\n  }, [token.address, token.chainId, recipient, amount]);\n\n  React.useEffect(() => {\n    setRecentAddresses(getRecentSendAddresses(walletAddress));\n  }, [walletAddress]);\n\n  // ── Actions ──────────────────────────────────────────────────────────\n  const setRecipient = React.useCallback((value: string) => {\n    setRecipientState(value);\n    setFieldErrors((prev) => ({ ...prev, recipient: undefined }));\n  }, []);\n\n  const setAmount = React.useCallback((value: string) => {\n    setAmountState(value);\n    setFieldErrors((prev) => ({ ...prev, amount: undefined }));\n  }, []);\n\n  const setMax = React.useCallback(() => {\n    // Fill the *displayed* (floor-truncated) balance, not full precision — so\n    // the amount the user sends matches what the Balance row shows. Strip the\n    // thousands separators formatWei adds so the value stays parseable.\n    setAmountState(balanceText.replace(/,/g, \"\"));\n    setFieldErrors((prev) => ({ ...prev, amount: undefined }));\n  }, [balanceText]);\n\n  const selectToken = React.useCallback(\n    (next: SendAsset) => {\n      setToken(next);\n      onTokenChange?.(next);\n      trackFunnelOnce(\"token_selected\", { tags: { item_id: next.symbol } });\n      setStep(\"recipient\");\n    },\n    [onTokenChange, trackFunnelOnce],\n  );\n\n  const goToTokenSelect = React.useCallback(() => setStep(\"token\"), []);\n\n  const back = React.useCallback(() => {\n    setStep((current) => {\n      if (current === \"result\") return \"review\";\n      if (current === initialStep) {\n        onExit?.();\n        return current;\n      }\n      const idx = STEP_ORDER.indexOf(current);\n      if (idx <= 0) {\n        onExit?.();\n        return current;\n      }\n      return STEP_ORDER[idx - 1];\n    });\n  }, [initialStep, onExit]);\n\n  // recipient → amount\n  const submitRecipient = React.useCallback(() => {\n    const { errors } = validateSend({\n      recipient,\n      amount: \"1\", // amount validated on its own step; only check recipient here\n      token,\n      balanceRaw,\n    });\n    if (errors.recipient) {\n      setFieldErrors((prev) => ({ ...prev, recipient: errors.recipient }));\n      return;\n    }\n    setFieldErrors((prev) => ({ ...prev, recipient: undefined }));\n    trackFunnelOnce(\"recipient_entered\");\n    setStep(\"amount\");\n  }, [recipient, token, balanceRaw, trackFunnelOnce]);\n\n  const selectRecipient = React.useCallback(\n    (address: `0x${string}`) => {\n      setRecipientState(address);\n      setFieldErrors((prev) => ({ ...prev, recipient: undefined }));\n      trackFunnelOnce(\"recipient_entered\");\n      setStep(\"amount\");\n    },\n    [trackFunnelOnce],\n  );\n\n  // amount → review\n  const submitAmount = React.useCallback(() => {\n    const result = validateSend({ recipient, amount, token, balanceRaw });\n    setFieldErrors(result.errors);\n    if (\n      hasFieldErrors(result.errors) ||\n      result.rawAmount === undefined ||\n      !result.recipient\n    ) {\n      return;\n    }\n    setReviewRawAmount(result.rawAmount);\n    setReviewRecipient(result.recipient);\n    setGasEstimate(null);\n    setGasError(\"\");\n    setStatus(\"idle\");\n    trackFunnelOnce(\"amount_entered\");\n    setStep(\"review\");\n  }, [recipient, amount, token, balanceRaw, trackFunnelOnce]);\n\n  // Review 단계에서 가스 추정을 한 번 트리거한다. chainInfo 가 아직 로딩 중이면\n  // 효과가 다시 돌면서 자동으로 늦게 발사된다.\n  //\n  // 중요: cleanup / cancelled 플래그를 두지 않는다 (develop 의 SendPage 와 동일).\n  // staleness 는 오직 gasReqIdRef 로만 판별한다. chainInfo 가 비동기로 로드되며\n  // 의존성이 바뀌어도, 진행 중인 추정 요청을 취소하면 안 된다 — 가드(gasLoading)\n  // 가 새 요청을 막는 사이 in-flight 요청만 버려져 UI 가 \"…\" 에 영구히 갇힌다.\n  React.useEffect(() => {\n    if (step !== \"review\") return;\n    if (!reviewRecipient || reviewRawAmount === null) return;\n    if (gasEstimate !== null || gasLoading || gasError) return;\n    if (!repository.canEstimateGas(chainInfo?.rpc)) return;\n\n    const reqId = ++gasReqIdRef.current;\n    setGasLoading(true);\n    setGasError(\"\");\n    void (async () => {\n      try {\n        const args = buildSendTxArgs({\n          token,\n          walletAddress,\n          recipient: reviewRecipient,\n          rawAmount: reviewRawAmount,\n        });\n        const estimate = await repository.estimateGas(args, chainInfo?.rpc);\n        if (gasReqIdRef.current !== reqId) return;\n        setGasEstimate(estimate);\n      } catch (err) {\n        if (gasReqIdRef.current !== reqId) return;\n        setGasError(\n          formatSendError(err, errorCtxRef.current, \"Failed to estimate gas.\"),\n        );\n      } finally {\n        if (gasReqIdRef.current === reqId) setGasLoading(false);\n      }\n    })();\n  }, [\n    step,\n    reviewRecipient,\n    reviewRawAmount,\n    gasEstimate,\n    gasLoading,\n    gasError,\n    repository,\n    chainInfo?.rpc,\n    token,\n    walletAddress,\n  ]);\n\n  // Gas affordability — native sends only. The fee is paid in the same coin\n  // being transferred, so `amount + est. fee` must fit the balance. ERC-20\n  // sends pay gas in the native coin (a different balance we don't hold here),\n  // so this check doesn't apply to them.\n  const insufficientGas = React.useMemo(() => {\n    if (!isNativeSendAsset(token)) return false;\n    if (!gasEstimate || reviewRawAmount === null) return false;\n    const feeWei = getEstTxFeeWei(gasEstimate, isBscLegacyChainId(token.chainId));\n    if (feeWei === null) return false;\n    return reviewRawAmount + feeWei > balanceRaw;\n  }, [token, gasEstimate, reviewRawAmount, balanceRaw]);\n\n  const confirmSend = React.useCallback(async () => {\n    if (!reviewRecipient || reviewRawAmount === null) return;\n    if (insufficientGas) return;\n    setError(\"\");\n    setTxHash(\"\");\n    setStatus(\"submitting\");\n    trackFunnelOnce(\"submitted\");\n    try {\n      let submittedHash: `0x${string}` | \"\" = \"\";\n      const outcome = await executeSend({\n        repository,\n        args: buildSendTxArgs({\n          token,\n          walletAddress,\n          recipient: reviewRecipient,\n          rawAmount: reviewRawAmount,\n        }),\n        chainId: token.chainId,\n        onSubmitted: (hash) => {\n          submittedHash = hash;\n          setTxHash(hash);\n          setStatus(\"confirming\");\n          setRecentAddresses(saveRecentSendAddress(walletAddress, reviewRecipient));\n          onSubmitted?.({\n            txHash: hash,\n            chainId: token.chainId,\n            amount,\n            tokenSymbol: token.symbol,\n            recipient: reviewRecipient,\n          });\n        },\n      });\n      if (outcome === \"success\") {\n        setStatus(\"success\");\n        trackFunnelOnce(\"confirmed\");\n        if (submittedHash && onSuccess) {\n          onSuccess?.(submittedHash);\n        } else {\n          setStep(\"result\");\n        }\n      } else if (outcome === \"reverted\") {\n        const message = \"Transaction reverted on-chain.\";\n        setStatus(\"error\");\n        setError(message);\n        trackFunnelOnce(\"confirmed\", {\n          status: \"failure\",\n          reason: normalizeFailureReason(new Error(message)),\n        });\n        if (submittedHash && onFailed) {\n          onFailed({ txHash: submittedHash, chainId: token.chainId, message });\n        } else {\n          setStep(\"result\");\n        }\n      } else {\n        const message = \"Transaction receipt was not found. Please check again later.\";\n        setStatus(\"error\");\n        setError(message);\n        trackFunnelOnce(\"confirmed\", { status: \"failure\", reason: \"timeout\" });\n        if (submittedHash && onFailed) {\n          onFailed({ txHash: submittedHash, chainId: token.chainId, message });\n        } else {\n          setStep(\"result\");\n        }\n      }\n    } catch (err) {\n      setStatus(\"error\");\n      trackFunnelOnce(\"confirmed\", {\n        status: \"failure\",\n        reason: normalizeFailureReason(err),\n      });\n      setError(\n        formatSendError(err, {\n          nativeSymbol: nativeSymbol || undefined,\n          networkName,\n          tokenSymbol: token.symbol,\n        }),\n      );\n      setStep(\"result\");\n    }\n  }, [\n    repository,\n    reviewRecipient,\n    reviewRawAmount,\n    insufficientGas,\n    token,\n    amount,\n    walletAddress,\n    onSubmitted,\n    onSuccess,\n    onFailed,\n    nativeSymbol,\n    networkName,\n    trackFunnelOnce,\n  ]);\n\n  const [hashCopied, setHashCopied] = React.useState(false);\n  const copyTimerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  React.useEffect(() => () => clearTimeout(copyTimerRef.current), []);\n\n  const copyHash = React.useCallback(async () => {\n    if (!txHash) return;\n    try {\n      await navigator.clipboard.writeText(txHash);\n      setHashCopied(true);\n      clearTimeout(copyTimerRef.current);\n      copyTimerRef.current = setTimeout(() => setHashCopied(false), 2000);\n    } catch {\n      /* copy is best-effort */\n    }\n  }, [txHash]);\n\n  const openTransaction = React.useCallback(() => {\n    if (!transactionUrl || !txHash) return;\n    const openTab = (target: string) => {\n      if (typeof window !== \"undefined\") {\n        window.open(target, \"_blank\", \"noopener,noreferrer\");\n      }\n    };\n    if (!onOutlink) {\n      openTab(transactionUrl);\n      return;\n    }\n    const maybe = onOutlink(transactionUrl, {\n      category: \"send\",\n      origin: \"send-transaction\",\n      payload: { chainId: token.chainId, txHash },\n    });\n    if (maybe && typeof (maybe as { then?: unknown }).then === \"function\") {\n      const win =\n        typeof window !== \"undefined\"\n          ? window.open(\"\", \"_blank\", \"noopener,noreferrer\")\n          : null;\n      (maybe as Promise<string | null | undefined>).then((result) => {\n        if (result === null) {\n          win?.close();\n          return;\n        }\n        const target = result ?? transactionUrl;\n        if (win) {\n          try {\n            win.location.href = target;\n          } catch {\n            win.close();\n            openTab(target);\n          }\n        } else {\n          openTab(target);\n        }\n      });\n      return;\n    }\n    const result = maybe as string | null | undefined;\n    if (result !== null) openTab(result ?? transactionUrl);\n  }, [onOutlink, token.chainId, transactionUrl, txHash]);\n\n  const retry = React.useCallback(() => {\n    setError(\"\");\n    setTxHash(\"\");\n    setStatus(\"idle\");\n    setGasEstimate(null);\n    setGasError(\"\");\n    funnelFiredRef.current.delete(\"submitted\");\n    funnelFiredRef.current.delete(\"confirmed\");\n    setStep(\"review\");\n  }, []);\n\n  return {\n    // state\n    step,\n    status,\n    token,\n    tokens: tokens.length > 0 ? tokens : [token],\n    recipient,\n    amount,\n    amountUsd,\n    fieldErrors,\n    error,\n    txHash,\n    gasEstimate,\n    gasLoading,\n    gasError,\n    insufficientGas,\n    reviewRawAmount,\n    reviewRecipient,\n    recentAddresses,\n    accounts,\n    accountName,\n    walletAddress,\n    resolvedEnv,\n    transactionUrl,\n    networkName,\n    nativeSymbol,\n    nativeDecimals: chainInfo?.currency_decimals ?? token.quantity.decimals,\n    balanceRaw,\n    balanceText,\n    priceUsd,\n    canSelectToken: (tokens.length > 0 ? tokens : [token]).length > 1,\n    canSend: repository.canSend,\n    getTokenPriceUsd,\n    // actions\n    setRecipient,\n    setAmount,\n    setMax,\n    selectToken,\n    goToTokenSelect,\n    selectRecipient,\n    submitRecipient,\n    submitAmount,\n    confirmSend,\n    retry,\n    back,\n    hashCopied,\n    copyHash,\n    openTransaction,\n  };\n}\n\nexport type SendFlow = ReturnType<typeof useSendFlow>;\n","import BigNumber from \"bignumber.js\";\n\n/**\n * Remove trailing '0' runs and an optional trailing '.' from a numeric string.\n *\n * Replaces the regex `/\\.?0+$/` which Sonar S5852 flags as a ReDoS risk\n * (false positive for this anchored pattern, but avoided entirely here).\n * Linear, backtracking-free; operates on char codes ('0' = 48, '.' = 46).\n */\nfunction stripTrailingZerosAndDot(s: string): string {\n  let end = s.length;\n  while (end > 0 && s.charCodeAt(end - 1) === 48) end--;\n  if (end > 0 && s.charCodeAt(end - 1) === 46) end--;\n  return s.slice(0, end);\n}\n\nexport function formatWei(value: string, decimals: number): string {\n  if (!value || value === \"0\") return \"0.00\";\n  const bn = new BigNumber(value).shiftedBy(-decimals);\n  const dp = bn.isGreaterThanOrEqualTo(1) ? 2 : 4;\n  return bn.toFormat(dp, BigNumber.ROUND_DOWN);\n}\n\nexport function shortenAddress(address: string): string {\n  if (address.length <= 10) return address;\n  return `${address.slice(0, 6)}...${address.slice(-4)}`;\n}\n\nexport function formatNumber(value: string | number): string {\n  const num = typeof value === \"string\" ? parseFloat(value) : value;\n  if (Number.isNaN(num)) return \"0\";\n  return num.toLocaleString(\"en-US\", {\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n  });\n}\n\nexport function formatPrice(value: string | number): string {\n  const num = typeof value === \"string\" ? parseFloat(value) : value;\n  if (Number.isNaN(num) || num === 0) return \"$0.00\";\n  const decimals = num >= 1 ? 2 : 4;\n  const factor = Math.pow(10, decimals);\n  const truncated = Math.floor(num * factor) / factor;\n  return `$${truncated.toLocaleString(\"en-US\", {\n    minimumFractionDigits: decimals,\n    maximumFractionDigits: decimals,\n  })}`;\n}\n\nexport function formatTokenValue(\n  rawQuantity: string,\n  tokenDecimals: number,\n  price: string,\n): string {\n  const bn = new BigNumber(rawQuantity).shiftedBy(-tokenDecimals);\n  const value = bn.multipliedBy(new BigNumber(price));\n  if (value.isZero() || value.isNaN()) return \"$0.00\";\n  const truncated = value\n    .integerValue(BigNumber.ROUND_DOWN)\n    .isGreaterThanOrEqualTo(1)\n    ? value.decimalPlaces(2, BigNumber.ROUND_DOWN)\n    : value.decimalPlaces(4, BigNumber.ROUND_DOWN);\n  return `$${truncated.toFormat()}`;\n}\n\n/**\n * 전체 자산 합계 등 큰 숫자에 적용하는 표기 규칙.\n * - 정수부 ≥ 1: 천단위 구분 + 소수 2자리 절사\n * - 0 < x < 1 & 소수 4자리 유효 숫자 존재: 4자리 절사\n * - 0 < x < 1 & 소수 4자리까지 전부 0: \"0.0001\" + isLowerBound=true\n */\nexport function formatTokenValueForDisplay(\n  val: string | number,\n): { displayValue: string; isLowerBound: boolean } {\n  if (val === \"\" || val === null || val === undefined) {\n    return { displayValue: \"0\", isLowerBound: false };\n  }\n\n  const raw = typeof val === \"number\" ? val.toString() : val;\n  if (raw === \"0\" || raw === \"0.0\") {\n    return { displayValue: \"0\", isLowerBound: false };\n  }\n\n  let newValue = raw;\n  if (newValue.includes(\"e\") || newValue.includes(\"E\")) {\n    const num = Number(newValue);\n    if (!Number.isFinite(num) || num === 0) {\n      return { displayValue: \"0\", isLowerBound: false };\n    }\n    newValue = stripTrailingZerosAndDot(num.toFixed(18));\n  }\n\n  const [integerPart = \"0\", fractionPart = \"\"] = newValue.split(\".\");\n\n  if (Number(integerPart) >= 1) {\n    const frac2 = fractionPart.slice(0, 2).padEnd(2, \"0\");\n    return {\n      displayValue: `${Number(integerPart).toLocaleString(\"en-US\")}.${frac2}`,\n      isLowerBound: false,\n    };\n  }\n\n  if (fractionPart.length < 4) {\n    return {\n      displayValue: `${integerPart}.${fractionPart}`,\n      isLowerBound: false,\n    };\n  }\n\n  const isTooSmall = fractionPart.slice(0, 4) === \"0000\";\n  if (isTooSmall) {\n    return { displayValue: \"0.0001\", isLowerBound: true };\n  }\n\n  return {\n    displayValue: `0.${fractionPart.slice(0, 4)}`,\n    isLowerBound: false,\n  };\n}\n","import { useMemo } from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport { CHAIN_IDS } from \"../../../lib/constants\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const CHAIN_INFO_QUERY_KEY = \"send:chain-info\";\n\ninterface ChainExplorerPostfix {\n  address?: string;\n  token?: string;\n  transaction?: string;\n}\n\ninterface ChainInfo {\n  chain: string;\n  chain_id: number;\n  currency_decimals: number;\n  currency_name: string;\n  currency_symbol: string;\n  explorer_url?: string;\n  info_url: string;\n  name: string;\n  network_id: number;\n  rpc: string;\n  short_name: string;\n  testnet: boolean;\n  explorer_postfix?: ChainExplorerPostfix;\n}\n\ninterface ChainInfoResponse {\n  code?: number;\n  message?: string;\n  // wallet API 의 /v1/public/chain/info/{chainId} 는 단일 객체가 아니라\n  // 항상 길이 1의 배열을 반환한다. 과거 단일 객체 형태와도 호환되게 둘 다 받는다.\n  data?: ChainInfo | ChainInfo[];\n}\n\nfunction getChainInfoPayload(json: ChainInfoResponse | ChainInfo): ChainInfo {\n  if (\"data\" in json && json.data !== undefined) {\n    const payload = json.data;\n    if (Array.isArray(payload)) {\n      const first = payload[0];\n      if (!first) throw new Error(\"Chain info payload is empty.\");\n      return first;\n    }\n    return payload;\n  }\n  return json as ChainInfo;\n}\n\nfunction joinExplorerPath(base: string, ...segments: string[]): string {\n  const normalizedBase = base.replace(/\\/+$/, \"\");\n  const normalizedSegments = segments\n    .map((segment) => segment.replace(/^\\/+|\\/+$/g, \"\"))\n    .filter(Boolean);\n\n  return [normalizedBase, ...normalizedSegments].join(\"/\");\n}\n\nfunction buildTransactionExplorerUrl(\n  env: Environment,\n  chainId: number,\n  chain: ChainInfo | undefined,\n  txHash: string | undefined,\n): string | undefined {\n  if (!txHash) return undefined;\n\n  if (chainId === CHAIN_IDS[env].cross) {\n    return getCrossTransactionExplorerUrl(chainId, txHash);\n  }\n\n  if (!chain?.explorer_url) {\n    return getFallbackTransactionExplorerUrl(env, chainId, txHash);\n  }\n\n  return joinExplorerPath(\n    chain.explorer_url,\n    chain.explorer_postfix?.transaction ?? \"tx\",\n    txHash,\n  );\n}\n\nfunction getCrossTransactionExplorerUrl(\n  chainId: number,\n  txHash: string,\n): string {\n  return joinExplorerPath(\n    \"https://explorer.onechain.nexus\",\n    String(chainId),\n    \"tx\",\n    txHash,\n  );\n}\n\nfunction getFallbackTransactionExplorerUrl(\n  env: Environment,\n  chainId: number,\n  txHash: string,\n): string | undefined {\n  const chains = CHAIN_IDS[env];\n\n  if (chainId === chains.cross) {\n    return getCrossTransactionExplorerUrl(chainId, txHash);\n  }\n\n  if (chainId === chains.bsc) {\n    const base =\n      env === \"production\"\n        ? \"https://bscscan.com\"\n        : \"https://testnet.bscscan.com\";\n    return joinExplorerPath(base, \"tx\", txHash);\n  }\n\n  return undefined;\n}\n\nexport function useTransactionExplorerUrl(\n  env: Environment,\n  chainId: number,\n  txHash: string | undefined,\n  enabled = true,\n) {\n  const query = useQuery<ChainInfo>({\n    queryKey: [CHAIN_INFO_QUERY_KEY, env, chainId],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"walletServer\", env);\n      const res = await fetch(`${baseUrl}/v1/public/chain/info/${chainId}`, {\n        cache: \"no-store\",\n        signal,\n      });\n      if (!res.ok) throw new Error(`Failed to fetch chain info: ${res.status}`);\n\n      const json = (await res.json()) as ChainInfoResponse | ChainInfo;\n      if (\"code\" in json && json.code !== undefined && json.code !== 200) {\n        throw new Error(`Chain info API error: ${json.code}`);\n      }\n\n      return getChainInfoPayload(json);\n    },\n    enabled,\n    staleTime: 5 * 60 * 1000,\n  });\n\n  const chainInfo = query.data;\n  const transactionUrl = useMemo(\n    () => buildTransactionExplorerUrl(env, chainId, chainInfo, txHash),\n    [chainId, chainInfo, env, txHash],\n  );\n\n  return { transactionUrl, chainInfo, ...query };\n}\n\n/**\n * 체인 정보를 1회 조회한 뒤, txHash별 익스플로러 URL을 만들어 주는 빌더를 반환.\n * 거래 이력 리스트처럼 행마다 URL이 필요한 곳에서 훅 규칙을 어기지 않고 쓰기 위한 헬퍼.\n */\nexport function useExplorerUrlBuilder(\n  env: Environment,\n  chainId: number,\n  enabled = true,\n) {\n  const { chainInfo } = useTransactionExplorerUrl(\n    env,\n    chainId,\n    undefined,\n    enabled,\n  );\n  const buildTxUrl = useMemo(\n    () => (txHash: string | undefined) =>\n      buildTransactionExplorerUrl(env, chainId, chainInfo, txHash),\n    [env, chainId, chainInfo],\n  );\n  return { buildTxUrl, chainInfo };\n}\n","import type { Environment } from \"../types\";\n\nexport const WALLET_API: Record<Environment, string> = {\n  dev: \"https://stg-wallet-server.crosstoken.io/api\",\n  stage: \"https://stg-wallet-server.crosstoken.io/api\",\n  production: \"https://wallet-server.crosstoken.io/api\",\n};\n\nexport const CHAIN_IDS: Record<\n  Environment,\n  { cross: number; bsc: number }\n> = {\n  dev: { cross: 612044, bsc: 97 },\n  stage: { cross: 612044, bsc: 97 },\n  production: { cross: 612055, bsc: 56 },\n};\n\n/**\n * Terms/Privacy 링크 폴백 URL. 호출부(dApp)가 `termsUrl` / `privacyUrl`을\n * 명시하지 않으면 dapp-ui가 이 CROSS 에코시스템 공통 문서로 내부 폴백해\n * 약관/정책 링크가 항상 노출되도록 한다. (WalletInfo / WalletConnectModal\n * 공통 단일 소스.)\n */\nexport const DEFAULT_TERMS_URL =\n  \"https://terms.onechain.nexus/docs/terms-july-23-2026\";\nexport const DEFAULT_PRIVACY_URL =\n  \"https://terms.onechain.nexus/docs/pp-july-23-2026\";\n\n/**\n * Bridge 약관 동의 모달(\"Agree to Terms of Service\")의 폴백 URL. 지갑 전체가\n * 아니라 ONEUSD 이용 약관에만 동의를 받는 화면이므로 CROSS 에코시스템 공통\n * 약관(`DEFAULT_TERMS_URL`)과 분리한다.\n */\nexport const DEFAULT_ONEUSD_TERMS_URL =\n  \"https://contents.crossdefi.io/Terms/terms/tos-26-07-09-oneusd.html\";\n","import type {\n  EstimateGasFn,\n  GetTransactionReceiptFn,\n  SendTransactionFn,\n} from \"../wallet-portfolio/types\";\nimport type { OnOutlink } from \"../../lib/outlink\";\nimport type { Environment, Theme } from \"../../types\";\n\nexport const NATIVE_TOKEN_SENTINEL_ADDRESS =\n  \"0x0000000000000000000000000000000000000001\";\n\nexport interface SendAsset {\n  name: string;\n  symbol: string;\n  chainId: number;\n  address: string;\n  quantity: {\n    decimals: number;\n    numeric: string;\n  };\n  icon_url: string;\n}\n\nexport type SendStatus =\n  | \"idle\"\n  | \"review\"\n  | \"submitting\"\n  | \"confirming\"\n  | \"success\"\n  | \"error\";\n\nexport interface RecentSendAddress {\n  address: `0x${string}`;\n  updatedAt: number;\n}\n\nexport interface SendAccount {\n  address: `0x${string}`;\n  index?: number;\n  name?: string;\n}\n\nexport interface SendPageProps {\n  env?: Environment;\n  theme?: Theme;\n  walletAddress: string;\n  accountName?: string;\n  accounts?: SendAccount[];\n  token: SendAsset;\n  tokens?: SendAsset[];\n  onTokenChange?: (token: SendAsset) => void;\n  sendTransaction?: SendTransactionFn;\n  getTransactionReceipt?: GetTransactionReceiptFn;\n  /**\n   * 확인 단계에서 표시할 가스/수수료 추정 함수. 주입되지 않으면 Gas/Est.Time/Max.Gas 행은 \"—\"로 표시된다.\n   * 사용자가 Send 버튼을 눌러 확인 화면에 진입할 때 한 번 호출된다.\n   */\n  estimateGas?: EstimateGasFn;\n  onSuccess?: (txHash: `0x${string}`) => void;\n  onConfirmSuccess?: (txHash?: `0x${string}`) => void;\n  onOutlink?: OnOutlink;\n}\n","import {\n  NATIVE_TOKEN_SENTINEL_ADDRESS,\n  type RecentSendAddress,\n  type SendAsset,\n} from \"./types\";\n\nconst ERC20_TRANSFER_SELECTOR = \"0xa9059cbb\";\nconst ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;\nconst RECENT_SEND_KEY_PREFIX = \"nexus-cross:dapp-ui:send:recent:\";\nconst MAX_RECENT_SEND_ADDRESSES = 10;\n\nfunction trimTrailingZeros(value: string): string {\n  let end = value.length;\n  while (end > 0 && value.charCodeAt(end - 1) === 48) end--;\n  return value.slice(0, end);\n}\n\nexport function normalizeAddress(address: string): `0x${string}` {\n  return address.toLowerCase() as `0x${string}`;\n}\n\nexport function isAddress(value: string): value is `0x${string}` {\n  return ADDRESS_RE.test(value);\n}\n\nexport function isNativeSendAsset(token: SendAsset): boolean {\n  return normalizeAddress(token.address) === NATIVE_TOKEN_SENTINEL_ADDRESS;\n}\n\nfunction getRecentSendStorageKey(walletAddress: string): string {\n  return `${RECENT_SEND_KEY_PREFIX}${walletAddress.toLowerCase()}`;\n}\n\nexport function getRecentSendAddresses(\n  walletAddress: string,\n): RecentSendAddress[] {\n  if (typeof window === \"undefined\") return [];\n\n  try {\n    const raw = window.localStorage.getItem(getRecentSendStorageKey(walletAddress));\n    if (!raw) return [];\n\n    const parsed = JSON.parse(raw) as RecentSendAddress[];\n    if (!Array.isArray(parsed)) return [];\n\n    return parsed\n      .filter((item): item is RecentSendAddress => {\n        return (\n          item !== null &&\n          typeof item === \"object\" &&\n          isAddress(item.address) &&\n          typeof item.updatedAt === \"number\"\n        );\n      })\n      .slice(0, MAX_RECENT_SEND_ADDRESSES);\n  } catch {\n    return [];\n  }\n}\n\nexport function saveRecentSendAddress(\n  walletAddress: string,\n  recipient: `0x${string}`,\n): RecentSendAddress[] {\n  if (typeof window === \"undefined\") return [];\n\n  const normalized = normalizeAddress(recipient);\n  const next = [\n    { address: normalized, updatedAt: Date.now() },\n    ...getRecentSendAddresses(walletAddress).filter(\n      (item) => item.address !== normalized,\n    ),\n  ].slice(0, MAX_RECENT_SEND_ADDRESSES);\n\n  try {\n    window.localStorage.setItem(\n      getRecentSendStorageKey(walletAddress),\n      JSON.stringify(next),\n    );\n  } catch {\n    return getRecentSendAddresses(walletAddress);\n  }\n\n  return next;\n}\n\nexport function clearRecentSendAddresses(walletAddress: string): void {\n  if (typeof window === \"undefined\") return;\n  try {\n    window.localStorage.removeItem(getRecentSendStorageKey(walletAddress));\n  } catch {\n    // Ignore storage failures so disconnect still proceeds.\n  }\n}\n\nexport function parseTokenAmount(\n  input: string,\n  decimals: number,\n): { value: bigint } | { error: string } {\n  const trimmed = input.trim();\n\n  if (!trimmed) return { error: \"Enter an amount.\" };\n  if (!/^\\d+(\\.\\d*)?$/.test(trimmed)) {\n    return { error: \"Enter a valid amount.\" };\n  }\n\n  const [wholePart = \"0\", fractionPart = \"\"] = trimmed.split(\".\");\n  if (fractionPart.length > decimals) {\n    return { error: `Max ${decimals} decimal places.` };\n  }\n\n  const whole = BigInt(wholePart || \"0\");\n  const paddedFraction = fractionPart.padEnd(decimals, \"0\");\n  const fraction = paddedFraction ? BigInt(paddedFraction) : 0n;\n  const unit = 10n ** BigInt(decimals);\n  const value = whole * unit + fraction;\n\n  if (value <= 0n) return { error: \"Amount must be greater than 0.\" };\n\n  return { value };\n}\n\nexport function formatRawTokenAmount(raw: string, decimals: number): string {\n  const value = BigInt(raw || \"0\");\n  const unit = 10n ** BigInt(decimals);\n  const whole = value / unit;\n  const fraction = value % unit;\n\n  if (fraction === 0n) return whole.toString();\n\n  const fractionText = trimTrailingZeros(\n    fraction.toString().padStart(decimals, \"0\"),\n  );\n\n  return `${whole.toString()}.${fractionText}`;\n}\n\nexport function encodeErc20Transfer(\n  recipient: `0x${string}`,\n  amount: bigint,\n): `0x${string}` {\n  const recipientWord = recipient\n    .replace(/^0x/, \"\")\n    .toLowerCase()\n    .padStart(64, \"0\");\n  const amountWord = amount.toString(16).padStart(64, \"0\");\n  return `${ERC20_TRANSFER_SELECTOR}${recipientWord}${amountWord}` as `0x${string}`;\n}\n","/**\n * Send feature — pure formatting helpers (wei → display units, gas fee math).\n * Extracted verbatim from the legacy SendPage so the new step screens and the\n * review screen share one source of truth. Policy mirrors crossy-sdk-js\n * `docs/06-gas-fee.md`.\n */\n\nimport type { GasEstimate } from \"../../wallet-portfolio/types\";\n\n/**\n * Wei → token-unit string, truncated to `maxDp` decimals (floor), trailing\n * zeros and a dangling \".\" removed.\n */\nexport function formatTokenUnits(\n  value: bigint,\n  decimals: number,\n  maxDp = 10,\n): string {\n  if (decimals <= 0) return value.toString();\n  const unit = 10n ** BigInt(decimals);\n  const whole = value / unit;\n  const fraction = value % unit;\n  if (fraction === 0n) return whole.toString();\n  const fractionPadded = fraction.toString().padStart(decimals, \"0\");\n  let trimmed = fractionPadded.slice(0, Math.max(0, Math.min(maxDp, decimals)));\n  trimmed = trimmed.replace(/0+$/, \"\");\n  return trimmed ? `${whole.toString()}.${trimmed}` : whole.toString();\n}\n\n/** Wei → native-coin amount: floor at 6dp, trim trailing zeros. */\nexport function formatWeiToNative(wei: bigint, decimals: number): string {\n  return formatTokenUnits(wei, decimals, 6);\n}\n\n/** Wei → Gwei: floor at 2dp, trim trailing zeros (per-gas-unit prices). */\nexport function formatWeiToGwei(wei: bigint): string {\n  return formatTokenUnits(wei, 9, 2);\n}\n\n/** Gas limit → comma-grouped integer (21000 → \"21,000\"). */\nexport function formatGasLimit(gasLimit: bigint): string {\n  return gasLimit.toLocaleString(\"en-US\");\n}\n\nexport type GasMode = \"dynamic\" | \"legacy\";\n\nexport function isBscLegacyChainId(chainId: number | undefined): boolean {\n  return chainId === 56 || chainId === 97;\n}\n\nexport function getGasPriceWei(estimate: GasEstimate): bigint | undefined {\n  return estimate.gasPrice ?? estimate.maxFeePerGas;\n}\n\n/** Dynamic (EIP-1559) when `maxFeePerGas` present, else Legacy. */\nexport function getGasMode(\n  estimate: GasEstimate,\n  forceLegacy = false,\n): GasMode | null {\n  if (forceLegacy) return getGasPriceWei(estimate) !== undefined ? \"legacy\" : null;\n  if (estimate.maxFeePerGas !== undefined) return \"dynamic\";\n  if (estimate.gasPrice !== undefined) return \"legacy\";\n  return null;\n}\n\n/** Est. Tx Fee = gasLimit × (maxFeePerGas | gasPrice), in wei. */\nexport function getEstTxFeeWei(\n  estimate: GasEstimate,\n  forceLegacy = false,\n): bigint | null {\n  if (forceLegacy) {\n    const gasPrice = getGasPriceWei(estimate);\n    return gasPrice !== undefined ? estimate.gasLimit * gasPrice : null;\n  }\n  if (estimate.maxFeePerGas !== undefined)\n    return estimate.gasLimit * estimate.maxFeePerGas;\n  if (estimate.gasPrice !== undefined) return estimate.gasLimit * estimate.gasPrice;\n  return null;\n}\n\n/** Max Gas = gasLimit × maxFeePerGas (dynamic) — null for legacy. */\nexport function getMaxGasWei(estimate: GasEstimate): bigint | null {\n  if (estimate.maxFeePerGas !== undefined)\n    return estimate.gasLimit * estimate.maxFeePerGas;\n  return null;\n}\n\n/** Format a USD value: `$1,234.56`. Returns `$0.00` for 0 / invalid. */\nexport function formatUsd(value: number): string {\n  if (!Number.isFinite(value) || value <= 0) return \"$0.00\";\n  return `$${value.toLocaleString(\"en-US\", {\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n  })}`;\n}\n","/**\n * Maps viem / wagmi / wallet-provider errors thrown during gas estimation\n * or transaction submission into short, user-friendly sentences shown in\n * the Send screen.\n *\n * We deliberately do NOT consume `viem`'s typed error classes\n * (`InsufficientFundsError`, `UserRejectedRequestError`, …) directly:\n *\n *   - Errors that surface here may have travelled through wagmi /\n *     embedded-wallet RPCs / `JSON.stringify` round-trips, which can\n *     break `instanceof` checks.\n *   - Some wallet providers wrap or re-throw their own error subclasses.\n *\n * String-pattern matching on `name` / `shortMessage` / `message` covers\n * all of those paths. Unknown errors fall back to the raw message so we\n * never hide information the user might need to report.\n */\n\nexport interface SendErrorContext {\n  /** Gas-currency symbol (e.g. \"BNB\", \"ETH\"). Used to clarify which asset is insufficient. */\n  nativeSymbol?: string;\n  /** Human-readable network name (e.g. \"BSC Testnet\"). */\n  networkName?: string;\n  /** The token symbol being sent (e.g. \"ONE\"). */\n  tokenSymbol?: string;\n}\n\ntype LooseError = {\n  name?: unknown;\n  message?: unknown;\n  shortMessage?: unknown;\n  details?: unknown;\n  cause?: unknown;\n};\n\n/**\n * Pulls every string field viem-style errors might carry into one\n * lowercased haystack. Walks `cause` chains because wagmi often wraps\n * the original viem error one or two layers deep.\n */\nfunction collectErrorText(err: unknown, depth = 0): string {\n  if (depth > 4 || err === null || err === undefined) return '';\n  if (typeof err === 'string') return err.toLowerCase();\n  if (typeof err !== 'object') return '';\n\n  const e = err as LooseError;\n  const parts: string[] = [];\n  if (typeof e.name === 'string') parts.push(e.name);\n  if (typeof e.shortMessage === 'string') parts.push(e.shortMessage);\n  if (typeof e.message === 'string') parts.push(e.message);\n  if (typeof e.details === 'string') parts.push(e.details);\n  if (e.cause !== undefined) parts.push(collectErrorText(e.cause, depth + 1));\n\n  return parts.join(' \\n ').toLowerCase();\n}\n\nfunction rawMessage(err: unknown): string {\n  if (err instanceof Error) return err.message;\n  if (typeof err === 'string') return err;\n  if (err && typeof err === 'object') {\n    const e = err as LooseError;\n    if (typeof e.shortMessage === 'string') return e.shortMessage;\n    if (typeof e.message === 'string') return e.message;\n  }\n  return '';\n}\n\n/**\n * Translates a Send-flow error into a one-line message safe to render\n * inline. Falls back to the original error message when no pattern\n * matches so the user can still file a bug report with full context.\n */\nexport function formatSendError(\n  err: unknown,\n  context: SendErrorContext = {},\n  fallback = 'Failed to send transaction.',\n): string {\n  const haystack = collectErrorText(err);\n  if (!haystack) return fallback;\n\n  const nativeSymbol = context.nativeSymbol?.trim() || 'gas token';\n  const networkName = context.networkName?.trim();\n\n  // ── User-cancelled ────────────────────────────────────────────────────\n  // Cover viem (`UserRejectedRequestError`), MetaMask 4001, embedded\n  // wallet \"user rejected\" wording, and assorted custom shapes.\n  if (\n    haystack.includes('user rejected') ||\n    haystack.includes('userrejected') ||\n    haystack.includes('user denied') ||\n    haystack.includes('user cancelled') ||\n    haystack.includes('user canceled') ||\n    haystack.includes('action_rejected') ||\n    haystack.includes('rejected by user')\n  ) {\n    return 'Transaction was cancelled.';\n  }\n\n  // ── Insufficient gas-currency ────────────────────────────────────────\n  // viem `InsufficientFundsError`:\n  //   \"The total cost (gas * gas fee + value) of executing this\n  //    transaction exceeds the balance of the account.\"\n  // Provider variants: \"insufficient funds for gas\", \"intrinsic gas too\n  // low\" (different cause but same user fix on testnets).\n  if (\n    haystack.includes('insufficientfunds') ||\n    haystack.includes('insufficient funds') ||\n    haystack.includes('exceeds the balance of the account') ||\n    haystack.includes('insufficient balance for transfer')\n  ) {\n    const where = networkName ? ` on ${networkName}` : '';\n    return (\n      `Not enough ${nativeSymbol} to cover the gas fee.` + \n      `Please top up your ${nativeSymbol} balance and try again.`\n    );\n  }\n\n  // ── Underpriced / replacement-too-low ────────────────────────────────\n  if (\n    haystack.includes('transaction underpriced') ||\n    haystack.includes('replacement transaction underpriced') ||\n    haystack.includes('fee too low')\n  ) {\n    return 'Gas fee was too low for the network. Please try again.';\n  }\n\n  // ── Nonce drift ──────────────────────────────────────────────────────\n  if (\n    haystack.includes('nonce too low') ||\n    haystack.includes('nonce has already been used') ||\n    haystack.includes('nonce too high')\n  ) {\n    return 'Pending transactions are out of order. Refresh the page and try again.';\n  }\n\n  // ── Reverted on-chain (or pre-flight simulation revert) ──────────────\n  if (\n    haystack.includes('execution reverted') ||\n    haystack.includes('contractfunctionrevertederror') ||\n    haystack.includes('contractfunctionexecutionerror')\n  ) {\n    return 'The transaction was rejected by the contract. The recipient or token may be invalid.';\n  }\n\n  // ── Chain not registered in wagmi ────────────────────────────────────\n  // (\"Chain X is not configured\" — usually a misconfigured DApp.)\n  if (haystack.match(/chain \\d+ is not configured/)) {\n    return 'This network is not registered in the app. Contact support.';\n  }\n\n  // ── RPC / network connectivity ───────────────────────────────────────\n  if (\n    haystack.includes('http request failed') ||\n    haystack.includes('failed to fetch') ||\n    haystack.includes('network request failed') ||\n    haystack.includes('timeout') ||\n    haystack.includes('econnreset') ||\n    haystack.includes('rpc error') ||\n    haystack.includes('429')\n  ) {\n    return 'Network connection failed. Check your internet and try again.';\n  }\n\n  // ── Gas estimate failed (generic viem `EstimateGasExecutionError`) ───\n  // Run this last because the other patterns above are also strictly\n  // gas-estimate failures we want to surface specifically.\n  if (\n    haystack.includes('estimategas') ||\n    haystack.includes('gas estimation failed')\n  ) {\n    return 'Could not estimate the gas fee. The recipient address or amount may be invalid.';\n  }\n\n  // ── Unknown — surface the raw short message so a bug report stays useful.\n  return rawMessage(err) || fallback;\n}\n","import type {\n  EstimateGasArgs,\n  GasEstimate,\n} from \"../../wallet-portfolio/types\";\n\n// 외부에서 estimateGas 함수를 주입하지 않는 dApp (예: 직접 WalletInfo/SendPage\n// 사용) 을 위해, chain-info API 가 알려준 RPC URL 로 raw JSON-RPC 호출을 보내\n// 가스 정보를 추정한다.\n//\n// 정책: crossy-sdk-js `docs/06-gas-fee.md` §2 / §4 와 동일.\n//   1. eth_estimateGas       → gasLimit\n//   2. eth_getBlockByNumber  → baseFeePerGas 존재?\n//        ├ 존재 → Dynamic: maxPriorityFeePerGas (eth_maxPriorityFeePerGas\n//        │         또는 1 Gwei 폴백), maxFeePerGas = baseFee + priority\n//        └ 없음 → Legacy:  eth_gasPrice (또는 2 Gwei 폴백)\n//   3. BSC(56) / BSC Testnet(97) 은 EIP-1559 미지원 — 강제 Legacy\n//\n// 모든 wei-scale 값은 BigInt 로 처리한다 (CLAUDE.md `NUMERIC_HANDLING`).\n\nconst LEGACY_GAS_PRICE_FALLBACK = 2_000_000_000n; // 2 Gwei\nconst DYNAMIC_PRIORITY_FALLBACK = 1_000_000_000n; // 1 Gwei\nconst RPC_TIMEOUT_MS = 8_000;\n\ninterface JsonRpcResponse<T> {\n  jsonrpc: \"2.0\";\n  id: number | string;\n  result?: T;\n  error?: { code: number; message: string };\n}\n\nasync function rpcCall<T>(\n  rpcUrl: string,\n  method: string,\n  params: unknown[],\n): Promise<T> {\n  const controller = new AbortController();\n  const timeout = setTimeout(() => controller.abort(), RPC_TIMEOUT_MS);\n  const res = await fetch(rpcUrl, {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/json\" },\n    body: JSON.stringify({ jsonrpc: \"2.0\", id: 1, method, params }),\n    signal: controller.signal,\n  }).finally(() => clearTimeout(timeout));\n  if (!res.ok) {\n    throw new Error(`RPC ${method} HTTP ${res.status}`);\n  }\n  const json = (await res.json()) as JsonRpcResponse<T>;\n  if (json.error) {\n    throw new Error(`RPC ${method}: ${json.error.message}`);\n  }\n  if (json.result === undefined) {\n    throw new Error(`RPC ${method}: empty result`);\n  }\n  return json.result;\n}\n\nfunction hexToBigInt(hex: string): bigint {\n  return BigInt(hex);\n}\n\nfunction bigIntToHex(value: bigint): `0x${string}` {\n  return `0x${value.toString(16)}`;\n}\n\nfunction buildTxParam(args: EstimateGasArgs): Record<string, string> {\n  const tx: Record<string, string> = { to: args.to };\n  if (args.from) tx.from = args.from;\n  if (args.value !== undefined && args.value > 0n) {\n    tx.value = bigIntToHex(args.value);\n  }\n  if (args.data) tx.data = args.data;\n  return tx;\n}\n\nfunction isLegacyOnlyChain(chainId: number | undefined): boolean {\n  return chainId === 56 || chainId === 97;\n}\n\ninterface FeeFields {\n  gasPrice?: bigint;\n  maxFeePerGas?: bigint;\n  maxPriorityFeePerGas?: bigint;\n}\n\nasync function fetchLegacyFees(rpcUrl: string): Promise<FeeFields> {\n  try {\n    const hex = await rpcCall<string>(rpcUrl, \"eth_gasPrice\", []);\n    return { gasPrice: hexToBigInt(hex) };\n  } catch {\n    return { gasPrice: LEGACY_GAS_PRICE_FALLBACK };\n  }\n}\n\nasync function fetchDynamicFees(\n  rpcUrl: string,\n  baseFee: bigint,\n): Promise<FeeFields> {\n  let priority = DYNAMIC_PRIORITY_FALLBACK;\n  try {\n    const hex = await rpcCall<string>(rpcUrl, \"eth_maxPriorityFeePerGas\", []);\n    priority = hexToBigInt(hex);\n  } catch {\n    // node가 EIP-1559 method 미지원 → 1 Gwei 폴백 유지\n  }\n  return {\n    maxFeePerGas: baseFee + priority,\n    maxPriorityFeePerGas: priority,\n  };\n}\n\nasync function fetchFees(\n  rpcUrl: string,\n  legacyOnly: boolean,\n): Promise<FeeFields> {\n  if (legacyOnly) return fetchLegacyFees(rpcUrl);\n\n  // Dynamic 우선: latest 블록의 baseFeePerGas 유무로 판별.\n  let baseFeeHex: string | undefined;\n  try {\n    const block = await rpcCall<{ baseFeePerGas?: string } | null>(\n      rpcUrl,\n      \"eth_getBlockByNumber\",\n      [\"latest\", false],\n    );\n    baseFeeHex = block?.baseFeePerGas;\n  } catch {\n    // block 조회 실패 → legacy 폴백으로 떨어진다\n  }\n\n  if (baseFeeHex) {\n    return fetchDynamicFees(rpcUrl, hexToBigInt(baseFeeHex));\n  }\n  return fetchLegacyFees(rpcUrl);\n}\n\nexport async function fallbackEstimateGas(\n  rpcUrl: string,\n  args: EstimateGasArgs,\n): Promise<GasEstimate> {\n  const legacyOnly = isLegacyOnlyChain(args.chainId);\n  const txParam = buildTxParam(args);\n\n  // gasLimit 추정과 fee 조회는 서로 독립적이므로 병렬 실행\n  // (gas-fee 문서 §2 \"병렬 RPC 최적화\" 와 같은 패턴).\n  const [gasLimitHex, fees] = await Promise.all([\n    rpcCall<string>(rpcUrl, \"eth_estimateGas\", [txParam]),\n    fetchFees(rpcUrl, legacyOnly),\n  ]);\n\n  return {\n    gasLimit: hexToBigInt(gasLimitHex),\n    gasPrice: fees.gasPrice,\n    maxFeePerGas: fees.maxFeePerGas,\n    maxPriorityFeePerGas: fees.maxPriorityFeePerGas,\n  };\n}\n","/**\n * Send feature — Repository port.\n *\n * Abstracts every side effect the Send flow needs (gas estimation, submitting\n * the transaction, polling a receipt) behind one interface so the use-cases /\n * UI never touch wagmi / RPC directly. `createSendRepository` adapts the\n * functions a host passes into `<SendFlow>` (formerly `<SendPage>` props) and\n * folds the \"injected estimateGas else RPC fallback\" decision in one place.\n */\n\nimport type {\n  EstimateGasArgs,\n  EstimateGasFn,\n  GasEstimate,\n  GetTransactionReceiptFn,\n  SendTransactionFn,\n} from \"../../wallet-portfolio/types\";\nimport { fallbackEstimateGas } from \"../utils/gas-rpc\";\n\nexport interface SendRepository {\n  /** True when a transaction can actually be submitted. */\n  readonly canSend: boolean;\n  /** True when a gas estimate can be produced (injected fn or an RPC fallback). */\n  canEstimateGas(rpcFallbackUrl?: string): boolean;\n  /**\n   * Estimate gas for the built tx args. Uses the injected `estimateGas` when\n   * present, otherwise an RPC fallback against `rpcFallbackUrl`.\n   * Throws when neither path is available.\n   */\n  estimateGas(args: EstimateGasArgs, rpcFallbackUrl?: string): Promise<GasEstimate>;\n  /** Submit the transaction; resolves to the tx hash. Throws when unsupported. */\n  sendTransaction(args: EstimateGasArgs): Promise<`0x${string}`>;\n  /** Fetch a receipt (null until mined). Absent when the host can't poll. */\n  getTransactionReceipt?: GetTransactionReceiptFn;\n}\n\nexport interface SendRepositoryDeps {\n  estimateGas?: EstimateGasFn;\n  sendTransaction?: SendTransactionFn;\n  getTransactionReceipt?: GetTransactionReceiptFn;\n}\n\nexport function createSendRepository(deps: SendRepositoryDeps): SendRepository {\n  const { estimateGas, sendTransaction, getTransactionReceipt } = deps;\n  return {\n    canSend: Boolean(sendTransaction),\n    canEstimateGas(rpcFallbackUrl) {\n      return Boolean(estimateGas) || Boolean(rpcFallbackUrl);\n    },\n    async estimateGas(args, rpcFallbackUrl) {\n      // 주입된 estimateGas 가 단일 진실원천(single source of truth) — 있으면\n      // 그것만 쓴다 (develop SendPage 와 동일). 둘을 race 하면 maxFeePerGas\n      // 계산식 차이(viem 은 baseFee×1.2+priority, RPC 폴백은 baseFee+priority)로\n      // 표기 값이 호출마다 들쭉날쭉해진다. 주입분이 없을 때만 RPC 폴백.\n      if (estimateGas) return estimateGas(args);\n      if (rpcFallbackUrl) return fallbackEstimateGas(rpcFallbackUrl, args);\n      throw new Error(\"Gas estimation is not available.\");\n    },\n    async sendTransaction(args) {\n      if (!sendTransaction) {\n        throw new Error(\"Sending is not available for this wallet.\");\n      }\n      return sendTransaction(args);\n    },\n    getTransactionReceipt,\n  };\n}\n","/**\n * Send feature — use-cases (UI-agnostic).\n *\n * Pure orchestration extracted verbatim from the legacy `SendPage`:\n *   - `validateSend`     : recipient / amount / token validation\n *   - `buildSendTxArgs`  : native vs ERC-20 transfer args (single source of\n *                          truth shared by gas-estimate and send)\n *   - `executeSend`      : submit + poll receipt → success / reverted / timeout\n *\n * No React, no DOM. Side effects go through {@link SendRepository}.\n */\n\nimport type {\n  EstimateGasArgs,\n  TransactionReceiptResult,\n} from \"../../wallet-portfolio/types\";\nimport type { SendAsset } from \"../types\";\nimport {\n  encodeErc20Transfer,\n  isAddress,\n  isNativeSendAsset,\n  normalizeAddress,\n  parseTokenAmount,\n} from \"../utils\";\nimport type { SendRepository } from \"./SendRepository\";\n\nexport const RECEIPT_POLL_INTERVAL_MS = 1500;\nexport const RECEIPT_POLL_MAX_ATTEMPTS = 120;\n\nexport interface SendFieldErrors {\n  token?: string;\n  recipient?: string;\n  amount?: string;\n}\n\nexport function hasFieldErrors(errors: SendFieldErrors): boolean {\n  return Boolean(errors.token || errors.recipient || errors.amount);\n}\n\n// ---------------------------------------------------------------------------\n// validateSend\n// ---------------------------------------------------------------------------\n\nexport interface ValidateSendInput {\n  recipient: string;\n  amount: string;\n  token: SendAsset;\n  /** Raw on-chain balance (wei) for the token, used for the over-balance check. */\n  balanceRaw: bigint;\n}\n\nexport interface ValidateSendResult {\n  errors: SendFieldErrors;\n  /** Parsed amount in wei — present only when the amount is valid. */\n  rawAmount?: bigint;\n  /** Trimmed, checksum-agnostic recipient — present only when valid. */\n  recipient?: `0x${string}`;\n}\n\nexport function validateSend({\n  recipient,\n  amount,\n  token,\n  balanceRaw,\n}: ValidateSendInput): ValidateSendResult {\n  const errors: SendFieldErrors = {};\n  const trimmedRecipient = recipient.trim();\n  const parsed = parseTokenAmount(amount, token.quantity.decimals);\n  let rawAmount: bigint | undefined;\n\n  if (!trimmedRecipient) {\n    errors.recipient = \"Enter a recipient address.\";\n  } else if (!isAddress(trimmedRecipient)) {\n    errors.recipient = \"Enter a valid EVM recipient address.\";\n  }\n\n  if (\"error\" in parsed) {\n    errors.amount = parsed.error;\n  } else {\n    rawAmount = parsed.value;\n    if (rawAmount > balanceRaw) {\n      errors.amount = \"Amount exceeds available balance.\";\n    }\n  }\n\n  if (!isNativeSendAsset(token) && !isAddress(token.address)) {\n    errors.token = \"Token contract address is invalid.\";\n  }\n\n  return {\n    errors,\n    rawAmount: errors.amount ? undefined : rawAmount,\n    recipient:\n      trimmedRecipient && isAddress(trimmedRecipient)\n        ? (trimmedRecipient as `0x${string}`)\n        : undefined,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// buildSendTxArgs\n// ---------------------------------------------------------------------------\n\nexport interface BuildSendTxArgsInput {\n  token: SendAsset;\n  walletAddress: string;\n  recipient: `0x${string}`;\n  rawAmount: bigint;\n}\n\nexport function buildSendTxArgs({\n  token,\n  walletAddress,\n  recipient,\n  rawAmount,\n}: BuildSendTxArgsInput): EstimateGasArgs {\n  const from = walletAddress as `0x${string}`;\n  if (isNativeSendAsset(token)) {\n    return { to: recipient, value: rawAmount, chainId: token.chainId, from };\n  }\n  return {\n    to: normalizeAddress(token.address),\n    data: encodeErc20Transfer(recipient, rawAmount),\n    value: 0n,\n    chainId: token.chainId,\n    from,\n  };\n}\n\n// ---------------------------------------------------------------------------\n// executeSend\n// ---------------------------------------------------------------------------\n\nexport function isReceiptReverted(receipt: TransactionReceiptResult): boolean {\n  const { status } = receipt;\n  return (\n    status === \"reverted\" ||\n    status === \"0x0\" ||\n    status === 0 ||\n    status === 0n ||\n    status === false\n  );\n}\n\nconst delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** Terminal outcome of {@link executeSend} (send-level errors throw instead). */\nexport type SendExecOutcome = \"success\" | \"reverted\" | \"timeout\";\n\nexport interface ExecuteSendInput {\n  repository: SendRepository;\n  args: EstimateGasArgs;\n  chainId: number;\n  /**\n   * Called once the tx is broadcast (hash known) — before receipt polling.\n   * The flow hook uses this to flip into \"confirming\" and persist the recent\n   * address while receipt polling continues.\n   */\n  onSubmitted: (hash: `0x${string}`) => void;\n  pollIntervalMs?: number;\n  pollMaxAttempts?: number;\n}\n\n/**\n * Submit the transaction and poll for its receipt.\n * Resolves to a terminal outcome; throws on submit / network errors so the\n * caller can format them with transaction context.\n */\nexport async function executeSend({\n  repository,\n  args,\n  chainId,\n  onSubmitted,\n  pollIntervalMs = RECEIPT_POLL_INTERVAL_MS,\n  pollMaxAttempts = RECEIPT_POLL_MAX_ATTEMPTS,\n}: ExecuteSendInput): Promise<SendExecOutcome> {\n  const hash = await repository.sendTransaction(args);\n  onSubmitted(hash);\n\n  const { getTransactionReceipt } = repository;\n  if (!getTransactionReceipt) return \"success\";\n\n  for (let attempt = 0; attempt < pollMaxAttempts; attempt += 1) {\n    const receipt = await getTransactionReceipt({ hash, chainId }).catch(\n      () => null,\n    );\n    if (receipt) {\n      return isReceiptReverted(receipt) ? \"reverted\" : \"success\";\n    }\n    await delay(pollIntervalMs);\n  }\n\n  return \"timeout\";\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { formatUsd } from \"../../model/sendFormat\";\nimport type { SendFlow } from \"../../model/useSendFlow\";\nimport { parseTokenAmount } from \"../../utils\";\n\nexport function SendAmount({ flow }: { flow: SendFlow }) {\n  const { amount, setAmount, amountUsd, balanceText, balanceRaw, token, setMax } =\n    flow;\n  const inputRef = React.useRef<HTMLInputElement>(null);\n\n  const moveCursorToEnd = React.useCallback(() => {\n    requestAnimationFrame(() => {\n      const el = inputRef.current;\n      if (!el) return;\n      const len = el.value.length;\n      el.setSelectionRange(len, len);\n    });\n  }, []);\n\n  // 진입 시 포커스 + 커서 끝\n  React.useEffect(() => {\n    inputRef.current?.focus();\n    moveCursorToEnd();\n  }, [moveCursorToEnd]);\n\n  // amount가 빈 값이 되면 \"0.00\" 재표시 + 커서 끝\n  React.useEffect(() => {\n    if (amount === \"\") {\n      setHasInteracted(false);\n      moveCursorToEnd();\n    }\n  }, [amount, moveCursorToEnd]);\n\n  const overBalance = React.useMemo(() => {\n    const parsed = parseTokenAmount(amount, token.quantity.decimals);\n    return \"value\" in parsed && parsed.value > balanceRaw;\n  }, [amount, token.quantity.decimals, balanceRaw]);\n\n  const [hasInteracted, setHasInteracted] = React.useState(false);\n  const displayAmount = !hasInteracted && amount === \"\" ? \"0.00\" : amount;\n  const empty = amount.trim() === \"\" || amount.trim() === \"0\";\n\n  return (\n    <div className=\"sf-body sf-amount\">\n      <div className=\"sf-amount-center\">\n        <span className=\"sf-amount-usd\">\n          {amountUsd !== undefined ? formatUsd(amountUsd) : \" \"}\n        </span>\n        <div\n          className=\"sf-amount-input-wrap\"\n          data-over={overBalance || undefined}\n          data-empty={empty || undefined}\n          onClick={() => inputRef.current?.focus()}\n        >\n          <input\n            ref={inputRef}\n            className=\"sf-amount-input\"\n            value={displayAmount}\n            onChange={(e) => {\n              const v = e.target.value;\n              if (!hasInteracted) {\n                if (amount === \"\") {\n                  // 플레이스홀더 \"0.00\" 상태 — 백스페이스면 유지, 새 입력이면 \"0.00\" 제거\n                  if (v.length < 4) return;\n                  setHasInteracted(true);\n                  const stripped = v.startsWith(\"0.00\") ? v.slice(4) : v;\n                  setAmount(stripped);\n                } else {\n                  // setMax 등으로 채워진 상태 — 바로 편집 허용\n                  setHasInteracted(true);\n                  setAmount(v);\n                }\n              } else {\n                setAmount(v);\n              }\n            }}\n            placeholder=\"0.00\"\n            inputMode=\"decimal\"\n            autoComplete=\"off\"\n            aria-label={`Amount in ${token.symbol}`}\n            size={1}\n          />\n        </div>\n        <div className=\"sf-amount-balance\">\n          <span>Balance:</span>\n          <button\n            type=\"button\"\n            className=\"sf-amount-max\"\n            onClick={setMax}\n            data-track=\"amount_max\"\n          >\n            {balanceText} {token.symbol}\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { shortenAddress } from \"../../../../lib/format\";\nimport { getPortraitUrl } from \"../../../wallet-info/constants\";\nimport type { SendFlow } from \"../../model/useSendFlow\";\nimport { isAddress } from \"../../utils\";\nimport { ClearIcon, PasteIcon } from \"../icons\";\n\ntype Tab = \"recent\" | \"account\";\n\nfunction displayName(\n  account: { name?: string; index?: number },\n  fallbackIndex: number,\n): string {\n  return account.name || `Account ${(account.index ?? fallbackIndex) + 1}`;\n}\n\nfunction avatarFallback(label: string): string {\n  return label.trim().slice(0, 1).toUpperCase() || \"A\";\n}\n\nfunction normalizeAddress(address: string): string {\n  return address.toLowerCase();\n}\n\nexport function SendRecipient({ flow }: { flow: SendFlow }) {\n  const {\n    recipient,\n    setRecipient,\n    fieldErrors,\n    recentAddresses,\n    accounts,\n    walletAddress,\n    resolvedEnv,\n    selectRecipient,\n  } = flow;\n\n  const myAccounts = React.useMemo(\n    () =>\n      accounts.filter(\n        (a) => a.address.toLowerCase() !== walletAddress.toLowerCase(),\n      ),\n    [accounts, walletAddress],\n  );\n  const hasMyAccounts = myAccounts.length > 0;\n  const accountByAddress = React.useMemo(() => {\n    return new Map(myAccounts.map((account, index) => [\n      normalizeAddress(account.address),\n      { account, index },\n    ]));\n  }, [myAccounts]);\n  const [tab, setTab] = React.useState<Tab>(\"recent\");\n  const inputRef = React.useRef<HTMLTextAreaElement>(null);\n  React.useEffect(() => { inputRef.current?.focus(); }, []);\n  const portraitBg = \"1A1A2E\";\n\n\n\n  const handlePaste = React.useCallback(async () => {\n    try {\n      const text = await navigator.clipboard.readText();\n      if (text) setRecipient(text.trim());\n    } catch {\n      /* clipboard denied — user can type manually */\n    }\n  }, [setRecipient]);\n\n  const filled = recipient.trim().length > 0;\n  const valid = isAddress(recipient.trim());\n\n  return (\n    <div className=\"sf-recipient\">\n      <div className=\"sf-recipient-top\">\n      <div\n        className=\"sf-address-field\"\n        data-filled={filled || undefined}\n        data-invalid={fieldErrors.recipient || undefined}\n      >\n        <textarea\n          ref={inputRef}\n          className=\"sf-address-input\"\n          value={recipient}\n          onChange={(e) => setRecipient(e.target.value)}\n          placeholder=\"Enter the recipient's address\"\n          autoComplete=\"off\"\n          spellCheck={false}\n          aria-label=\"Recipient address\"\n          rows={filled ? 2 : 1}\n        />\n        {filled ? (\n          <button\n            type=\"button\"\n            className=\"sf-address-action\"\n            onClick={() => setRecipient(\"\")}\n            aria-label=\"Clear\"\n            data-track=\"recipient_clear\"\n          >\n            <ClearIcon size={20} />\n          </button>\n        ) : (\n          <button\n            type=\"button\"\n            className=\"sf-address-action\"\n            onClick={handlePaste}\n            aria-label=\"Paste from clipboard\"\n            data-track=\"recipient_paste\"\n          >\n            <PasteIcon size={20} />\n          </button>\n        )}\n      </div>\n      {fieldErrors.recipient && (\n        <p className=\"sf-field-error\" role=\"alert\">\n          {fieldErrors.recipient}\n        </p>\n      )}\n\n      <div className=\"sf-tabs\" role=\"tablist\">\n        <button\n          type=\"button\"\n          role=\"tab\"\n          className=\"sf-tab\"\n          data-active={tab === \"recent\" || undefined}\n          aria-selected={tab === \"recent\"}\n          onClick={() => setTab(\"recent\")}\n          data-track=\"recipient_tab\"\n          data-track-id=\"recent\"\n        >\n          Recent\n        </button>\n        {hasMyAccounts && (\n          <button\n            type=\"button\"\n            role=\"tab\"\n            className=\"sf-tab\"\n            data-active={tab === \"account\" || undefined}\n            aria-selected={tab === \"account\"}\n            onClick={() => setTab(\"account\")}\n            data-track=\"recipient_tab\"\n            data-track-id=\"account\"\n          >\n            My Account\n          </button>\n        )}\n      </div>\n      </div>\n\n      <div className=\"sf-address-list\">\n        {tab === \"account\" && hasMyAccounts\n          ? myAccounts.map((account, index) => {\n              const name = displayName(account, index);\n              const portrait = getPortraitUrl(resolvedEnv, account.address, {\n                size: 56,\n                bg: portraitBg,\n              });\n              return (\n                <button\n                  key={account.address}\n                  type=\"button\"\n                  className=\"sf-address-row\"\n                  onClick={() => selectRecipient(account.address)}\n                  data-track=\"recipient_item\"\n                >\n                  <span className=\"sf-address-avatar\">\n                    {portrait && (\n                      <img\n                        src={portrait}\n                        alt=\"\"\n                        width={40}\n                        height={40}\n                        draggable={false}\n                        onError={(e) => {\n                          e.currentTarget.style.display = \"none\";\n                        }}\n                      />\n                    )}\n                    <span className=\"sf-address-avatar-fallback\">\n                      {avatarFallback(name)}\n                    </span>\n                  </span>\n                  <span className=\"sf-address-meta\">\n                    <span className=\"sf-address-name\">{name}</span>\n                    <span className=\"sf-address-value\">\n                      {shortenAddress(account.address)}\n                    </span>\n                  </span>\n                </button>\n              );\n            })\n          : recentAddresses.length > 0\n            ? recentAddresses.map((item) => {\n                const matchedAccount = accountByAddress.get(\n                  normalizeAddress(item.address),\n                );\n                const name = matchedAccount\n                  ? displayName(matchedAccount.account, matchedAccount.index)\n                  : shortenAddress(item.address);\n                const portrait = getPortraitUrl(resolvedEnv, item.address, {\n                  size: 56,\n                  bg: portraitBg,\n                });\n                return (\n                  <button\n                    key={item.address}\n                    type=\"button\"\n                    className=\"sf-address-row\"\n                    onClick={() => selectRecipient(item.address)}\n                    data-track=\"recipient_item\"\n                  >\n                    <span className=\"sf-address-avatar\">\n                      {portrait && (\n                        <img\n                          src={portrait}\n                          alt=\"\"\n                          width={40}\n                          height={40}\n                          draggable={false}\n                          onError={(e) => {\n                            e.currentTarget.style.display = \"none\";\n                          }}\n                        />\n                      )}\n                      <span className=\"sf-address-avatar-fallback\">\n                        {avatarFallback(name)}\n                      </span>\n                    </span>\n                    <span className=\"sf-address-meta\">\n                      <span className=\"sf-address-name\">{name}</span>\n                      <span className=\"sf-address-value\">\n                        {shortenAddress(item.address)}\n                      </span>\n                    </span>\n                  </button>\n                );\n              })\n            : (\n              <div className=\"sf-empty\">No send history</div>\n            )}\n      </div>\n\n      {filled && !valid && !fieldErrors.recipient && (\n        <p className=\"sf-field-error\" role=\"alert\">\n          Enter a valid EVM recipient address.\n        </p>\n      )}\n    </div>\n  );\n\n}\n","import type { Environment } from \"../../types\";\nimport { CHAIN_IDS } from \"../../lib/constants\";\n\nexport { WALLET_API, CHAIN_IDS } from \"../../lib/constants\";\n\n/**\n * CROSSD 토큰 컨트랙트 주소 (mainnet / testnet). 주소 자체가 네트워크별로\n * 유일하므로 env 분기 없이 두 주소로만 CROSSD를 판별한다. 소문자로 비교한다.\n */\nexport const CROSSD_TOKEN_ADDRESSES = new Set(\n  [\n    \"0xff42c13d927a6b9265236619161accb227b6d452\", // mainnet\n    \"0x9364ea6790f6e0ecfaa5164085f2a7de34ec55fb\", // testnet\n  ].map((address) => address.toLowerCase()),\n);\n\n/** CROSSD 컨트랙트 주소로 판별한다 (mainnet/testnet). 대소문자 무시. */\nexport function isCrossdTokenAddress(address: string): boolean {\n  return CROSSD_TOKEN_ADDRESSES.has(address.toLowerCase());\n}\n\nexport const FORGE_API: Record<Environment, string> = {\n  dev: \"https://stg-bonding-curve-api.crosstoken.io/api\",\n  stage: \"https://stg-bonding-curve-api.crosstoken.io/api\",\n  production: \"https://bonding-curve-api.crosstoken.io/api\",\n};\n\nexport const CHAIN_BADGE_ICONS: Record<\"cross\" | \"bsc\", string> = {\n  cross: \"https://contents.crosstoken.io/wallet/token/images/CROSS.svg\",\n  bsc: \"https://contents.crosstoken.io/wallet/token/images/BSC.svg\",\n};\n\nexport function getChainBadgeUrl(\n  env: Environment,\n  chainId: number,\n): string | undefined {\n  const chains = CHAIN_IDS[env];\n  if (chainId === chains.cross) return CHAIN_BADGE_ICONS.cross;\n  if (chainId === chains.bsc) return CHAIN_BADGE_ICONS.bsc;\n  return undefined;\n}\n\n/** 지갑 주소로부터 자동 생성되는 프로필 이미지(portrait) 서비스 엔드포인트. */\nexport const PORTRAIT_API: Record<Environment, string> = {\n  dev: \"https://dev-portrait.crosstoken.io\",\n  stage: \"https://dev-portrait.crosstoken.io\",\n  production: \"https://portrait.crosstoken.io\",\n};\n\nexport interface PortraitUrlOptions {\n  /** 출력 이미지 크기(px). 기본 120. */\n  size?: number;\n  /** 배경색 hex (# 없이). 미지정 시 투명. */\n  bg?: string;\n  /** 파일 포맷. 기본 \"png\". */\n  format?: \"png\" | \"svg\";\n}\n\n/**\n * EVM 지갑 주소로부터 자동 생성되는 프로필 이미지(portrait) URL을 반환.\n * @example\n *   getPortraitUrl(\"stage\", \"0xd8dA...\", { size: 120, bg: \"1A1A2E\" })\n */\nexport function getPortraitUrl(\n  env: Environment,\n  address: string,\n  opts: PortraitUrlOptions = {},\n): string | undefined {\n  if (!address || !address.startsWith(\"0x\")) return undefined;\n  const { size = 120, bg, format = \"png\" } = opts;\n  const base = PORTRAIT_API[env];\n  const bgParam = bg ? `&bg=${bg}` : \"\";\n  return `${base}/portrait/evm/${address}.${format}?size=${size}${bgParam}`;\n}\n","import * as React from \"react\";\n\ninterface IconProps {\n  size?: number;\n  className?: string;\n}\n\nexport function SearchIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 20 20\" fill=\"none\" className={className} aria-hidden=\"true\">\n      <circle cx=\"9\" cy=\"9\" r=\"6\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n      <path d=\"m17 17-3.2-3.2\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\nexport function PasteIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" className={className} aria-hidden=\"true\">\n      <path\n        d=\"M9 2h6a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zM8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-2M12 11v6M9 14h6\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.7\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ClearIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 20 20\" fill=\"none\" className={className} aria-hidden=\"true\">\n      <circle cx=\"10\" cy=\"10\" r=\"10\" fill=\"currentColor\" opacity=\"0.12\" />\n      <path d=\"M7 7l6 6M13 7l-6 6\" stroke=\"currentColor\" strokeWidth=\"1.75\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\nexport function DownArrowIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 16 16\" fill=\"none\" className={className} aria-hidden=\"true\">\n      <path d=\"M8 3v10m0 0 4-4m-4 4-4-4\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n    </svg>\n  );\n}\n\nexport function SuccessCircleIcon({ size = 56, className }: IconProps) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 56 56\" fill=\"none\" className={className} aria-hidden=\"true\">\n      {/* 성공 체크 = 초록(teal) 램프. DS v1.0.0에서 accent/primary는 브랜드 주황이다. */}\n      <circle cx=\"28\" cy=\"28\" r=\"28\" fill=\"var(--ds-system-green-default, #00b4b7)\" />\n      <path\n        d=\"M17 28.5 24.2 35.5 39 20.5\"\n        stroke=\"var(--ds-system-green-on, #ffffff)\"\n        strokeWidth=\"4\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ErrorCircleIcon({ size = 56, className }: IconProps) {\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 56 56\" fill=\"none\" className={className} aria-hidden=\"true\">\n      <circle cx=\"28\" cy=\"28\" r=\"28\" fill=\"var(--ds-system-red-default, #da0b2e)\" />\n      <path d=\"M20 20 36 36m0-16L20 36\" stroke=\"#ffffff\" strokeWidth=\"4\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { shortenAddress } from \"../../../../lib/format\";\nimport { CheckIcon } from \"../../../wallet-info/components/icons\";\nimport type { SendFlow } from \"../../model/useSendFlow\";\nimport { ErrorCircleIcon, SuccessCircleIcon } from \"../icons\";\n\nexport function SendResult({ flow }: { flow: SendFlow }) {\n  const { status, txHash, error, copyHash, hashCopied, openTransaction } = flow;\n  const isError = status === \"error\";\n  const isConfirmed = status === \"success\";\n\n  if (isError) {\n    return (\n      <div className=\"sf-body sf-result sf-result--error\">\n        <span className=\"sf-result-icon\">\n          <ErrorCircleIcon size={56} />\n        </span>\n        <div className=\"sf-result-copy\">\n          <h3 className=\"sf-result-title\">Transaction Failed</h3>\n          <p className=\"sf-result-desc\">\n            {error || \"Something went wrong. Please try again.\"}\n          </p>\n        </div>\n        {txHash && (\n          <HashCard\n            txHash={txHash}\n            hashCopied={hashCopied}\n            onCopy={copyHash}\n            onOpen={openTransaction}\n          />\n        )}\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"sf-body sf-result\">\n      <span className=\"sf-result-icon\" data-pending={!isConfirmed || undefined}>\n        {isConfirmed ? <SuccessCircleIcon size={56} /> : <span className=\"sf-result-spinner\" />}\n      </span>\n      <div className=\"sf-result-copy\">\n        <h3 className=\"sf-result-title\">\n          {isConfirmed ? \"Transaction Complete\" : \"Transaction in Progress\"}\n        </h3>\n        <p className=\"sf-result-desc\">\n          {isConfirmed\n            ? \"Your transfer was sent successfully.\"\n            : \"Transfer in progress. Waiting for confirmation.\"}\n        </p>\n      </div>\n      {txHash && (\n        <HashCard\n          txHash={txHash}\n          hashCopied={hashCopied}\n          onCopy={copyHash}\n          onOpen={openTransaction}\n        />\n      )}\n    </div>\n  );\n}\n\nfunction HashCard({\n  txHash,\n  hashCopied,\n  onCopy,\n  onOpen,\n}: {\n  txHash: string;\n  hashCopied: boolean;\n  onCopy: () => void;\n  onOpen: () => void;\n}) {\n  return (\n    <div className=\"sf-result-hash-card\">\n      <span className=\"sf-result-hash-label\">Transaction Hash</span>\n      <div className=\"sf-result-hash-row\">\n        <button\n          type=\"button\"\n          className=\"sf-result-hash-link\"\n          onClick={onOpen}\n          aria-label=\"Open transaction in explorer\"\n          data-track=\"tx_link_open\"\n        >\n          {shortenAddress(txHash)}\n        </button>\n        <button\n          type=\"button\"\n          className=\"sf-result-copy-btn\"\n          onClick={onCopy}\n          aria-label=\"Copy transaction hash\"\n          data-track=\"tx_hash_copy\"\n        >\n          {hashCopied ? <CheckIcon size={16} /> : \"Copy\"}\n        </button>\n      </div>\n    </div>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { getPortraitUrl } from \"../../../wallet-info/constants\";\nimport {\n  formatGasLimit,\n  formatTokenUnits,\n  formatWeiToGwei,\n  formatWeiToNative,\n  getEstTxFeeWei,\n  getGasMode,\n  getGasPriceWei,\n  isBscLegacyChainId,\n} from \"../../model/sendFormat\";\nimport type { SendFlow } from \"../../model/useSendFlow\";\nimport { isNativeSendAsset } from \"../../utils\";\nimport { DownArrowIcon } from \"../icons\";\n\nconst PLACEHOLDER = \"—\";\n\nexport function SendReview({ flow }: { flow: SendFlow }) {\n  const {\n    token,\n    reviewRawAmount,\n    reviewRecipient,\n    accountName,\n    accounts,\n    walletAddress,\n    networkName,\n    nativeSymbol,\n    nativeDecimals,\n    gasEstimate,\n    gasLoading,\n    gasError,\n    insufficientGas,\n    error,\n    resolvedEnv,\n  } = flow;\n\n  const isNative = isNativeSendAsset(token);\n  const amountDisplay =\n    reviewRawAmount !== null\n      ? formatTokenUnits(reviewRawAmount, token.quantity.decimals)\n      : \"\";\n  const fromName = accountName?.trim() || `Account ${(accounts[0]?.index ?? 0) + 1}`;\n  const fromPortrait = getPortraitUrl(resolvedEnv, walletAddress, {\n    size: 56,\n    bg: \"1A1A2E\",\n  });\n\n  const displayNativeSymbol = nativeSymbol || (isNative ? token.symbol : \"\");\n  const forceLegacyGas = isBscLegacyChainId(token.chainId);\n  const gasMode = gasEstimate ? getGasMode(gasEstimate, forceLegacyGas) : null;\n  const estTxFeeWei = gasEstimate\n    ? getEstTxFeeWei(gasEstimate, forceLegacyGas)\n    : null;\n  const fee = (wei: bigint | null) =>\n    wei !== null && displayNativeSymbol\n      ? `${formatWeiToNative(wei, nativeDecimals)} ${displayNativeSymbol}`\n      : PLACEHOLDER;\n  const gasPrice = (wei: bigint | undefined) =>\n    wei !== undefined ? `${formatWeiToGwei(wei)} Gwei` : PLACEHOLDER;\n\n  const maxTotalText = (() => {\n    if (!isNative || !gasEstimate || estTxFeeWei === null || reviewRawAmount === null)\n      return PLACEHOLDER;\n    const sum = reviewRawAmount + estTxFeeWei;\n    return `${formatWeiToNative(sum, nativeDecimals)} ${displayNativeSymbol || token.symbol}`;\n  })();\n\n  const v = (text: string) => (gasLoading ? \"…\" : text);\n  const gasRows = [\n    { label: \"Est. Tx Fee\", value: v(fee(estTxFeeWei)) },\n    ...(gasMode === \"dynamic\"\n      ? [\n          {\n            label: \"Max Priority Fee\",\n            value: v(gasPrice(gasEstimate?.maxPriorityFeePerGas)),\n          },\n          {\n            label: \"Max Gas Fee\",\n            value: v(gasPrice(gasEstimate?.maxFeePerGas)),\n          },\n        ]\n      : gasMode === \"legacy\"\n        ? [\n            {\n              label: \"Gas Price\",\n              value: v(\n                gasPrice(\n                  gasEstimate ? getGasPriceWei(gasEstimate) : undefined,\n                ),\n              ),\n            },\n          ]\n        : []),\n    {\n      label: \"Gas Limit\",\n      value: v(gasEstimate ? formatGasLimit(gasEstimate.gasLimit) : PLACEHOLDER),\n    },\n  ];\n\n  return (\n    <div className=\"sf-body sf-review\">\n      <div className=\"sf-review-amount\">\n        <img\n          className=\"sf-review-amount-icon\"\n          src={token.icon_url}\n          alt=\"\"\n          width={40}\n          height={40}\n          draggable={false}\n          onError={(e) => {\n            e.currentTarget.src =\n              \"https://contents.crosstoken.io/wallet/token/images/FT.png\";\n          }}\n        />\n        <span className=\"sf-review-amount-value\">\n          {amountDisplay}\n          <span className=\"sf-review-amount-symbol\">{token.symbol}</span>\n        </span>\n      </div>\n\n      <div className=\"sf-review-parties\">\n        <div className=\"sf-review-card sf-review-party\">\n          <span className=\"sf-review-party-label\">From</span>\n          <div className=\"sf-review-party-head\">\n            <span className=\"sf-review-party-avatar\">\n              {fromPortrait && (\n                <img\n                  src={fromPortrait}\n                  alt=\"\"\n                  width={28}\n                  height={28}\n                  draggable={false}\n                  onError={(e) => {\n                    e.currentTarget.style.display = \"none\";\n                  }}\n                />\n              )}\n            </span>\n            <span className=\"sf-review-party-name\">{fromName}</span>\n          </div>\n          <span className=\"sf-review-party-addr\">{walletAddress}</span>\n        </div>\n        <span className=\"sf-review-arrow\" aria-hidden>\n          <DownArrowIcon size={16} />\n        </span>\n        <div className=\"sf-review-card sf-review-party\">\n          <span className=\"sf-review-party-label\">To</span>\n          <span className=\"sf-review-party-addr\">{reviewRecipient}</span>\n        </div>\n      </div>\n\n      <section className=\"sf-review-card\">\n        <h3 className=\"sf-review-section-title\">Summary</h3>\n        <div className=\"sf-review-row\">\n          <span className=\"sf-review-row-label\">Network</span>\n          <span className=\"sf-review-row-value\">{networkName}</span>\n        </div>\n        {gasRows.map((row) => (\n          <div className=\"sf-review-row\" key={row.label}>\n            <span className=\"sf-review-row-label\">{row.label}</span>\n            <span className=\"sf-review-row-value\">{row.value}</span>\n          </div>\n        ))}\n      </section>\n\n      <section className=\"sf-review-card\">\n        <h3 className=\"sf-review-section-title\">Total</h3>\n        <div className=\"sf-review-row\">\n          <span className=\"sf-review-row-label\">Total Amount</span>\n          <span className=\"sf-review-row-value\">\n            {amountDisplay} {token.symbol}\n          </span>\n        </div>\n        {isNative && (\n          <div className=\"sf-review-row\">\n            <span className=\"sf-review-row-label\">Max. Total Amount</span>\n            <span className=\"sf-review-row-value\">{v(maxTotalText)}</span>\n          </div>\n        )}\n      </section>\n\n      {insufficientGas && !gasError && !error && (\n        <div className=\"sf-message sf-message--error\" role=\"alert\">\n          Not enough {displayNativeSymbol || token.symbol} to cover the amount\n          plus the network fee. Reduce the amount and try again.\n        </div>\n      )}\n      {(gasError || error) && (\n        <div className=\"sf-message sf-message--error\" role=\"alert\">\n          {gasError || error}\n        </div>\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { formatWei } from \"../../../../lib/format\";\nimport { getChainBadgeUrl } from \"../../../wallet-info/constants\";\nimport { formatUsd } from \"../../model/sendFormat\";\nimport type { SendFlow } from \"../../model/useSendFlow\";\nimport type { SendAsset } from \"../../types\";\nimport { SearchIcon } from \"../icons\";\n\nexport function SendTokenSelect({ flow }: { flow: SendFlow }) {\n  const [query, setQuery] = React.useState(\"\");\n  const q = query.trim().toLowerCase();\n  const list = React.useMemo<SendAsset[]>(() => {\n    if (!q) return flow.tokens;\n    return flow.tokens.filter(\n      (t) =>\n        t.symbol.toLowerCase().includes(q) ||\n        t.name.toLowerCase().includes(q),\n    );\n  }, [flow.tokens, q]);\n\n  return (\n    <div className=\"sf-body sf-token-select\">\n      <div className=\"sf-search\">\n        <SearchIcon size={20} />\n        <input\n          className=\"sf-search-input\"\n          value={query}\n          onChange={(e) => setQuery(e.target.value)}\n          placeholder=\"Search Tokens\"\n          autoComplete=\"off\"\n          spellCheck={false}\n          aria-label=\"Search tokens\"\n        />\n      </div>\n\n      <div className=\"sf-token-list\">\n        {list.map((t) => {\n          const key = `${t.chainId}:${t.address.toLowerCase()}`;\n          const badge = getChainBadgeUrl(flow.resolvedEnv, t.chainId);\n          const price = flow.getTokenPriceUsd?.(t);\n          return (\n            <button\n              key={key}\n              type=\"button\"\n              className=\"sf-token-row\"\n              onClick={() => flow.selectToken(t)}\n              data-track=\"token_item\"\n              data-track-id={t.symbol}\n            >\n              <span className=\"sf-token-left\">\n                <span className=\"sf-token-icon-wrap\">\n                  <img\n                    className=\"sf-token-icon\"\n                    src={t.icon_url}\n                    alt=\"\"\n                    width={40}\n                    height={40}\n                    loading=\"lazy\"\n                    draggable={false}\n                    onError={(e) => {\n                      e.currentTarget.src =\n                        \"https://contents.crosstoken.io/wallet/token/images/FT.png\";\n                    }}\n                  />\n                  {badge && (\n                    <img\n                      className=\"sf-token-chain-badge\"\n                      src={badge}\n                      alt=\"\"\n                      aria-hidden\n                      width={16}\n                      height={16}\n                      loading=\"lazy\"\n                      draggable={false}\n                      onError={(e) => {\n                        e.currentTarget.style.display = \"none\";\n                      }}\n                    />\n                  )}\n                </span>\n                <span className=\"sf-token-name\">{t.symbol}</span>\n              </span>\n              <span className=\"sf-token-right\">\n                <span className=\"sf-token-balance\">\n                  {formatWei(t.quantity.numeric, t.quantity.decimals)}\n                </span>\n                {price !== undefined && (\n                  <span className=\"sf-token-price\">{formatUsd(price)}</span>\n                )}\n              </span>\n            </button>\n          );\n        })}\n        {list.length === 0 && <div className=\"sf-empty\">No tokens found</div>}\n      </div>\n    </div>\n  );\n}\n","import styleInject from '#style-inject';styleInject(\".sf-root{display:flex;flex-direction:column;flex:1;min-height:0;font-family:inherit}.sf-header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:24px;flex-shrink:0;position:relative;border-bottom:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.sf-header .sf-header-btn:last-child{position:absolute;top:24px;right:24px}.sf-header-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;flex-shrink:0;padding:0;border:none;background:transparent;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer;filter:none;transition:color .15s ease}.sf-header-btn:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85));filter:none}.sf-header-title{position:absolute;left:50%;transform:translate(-50%);text-align:center;font-family:inherit;font-size:18px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-highest, #000000);pointer-events:none}.sf-body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.15) transparent;overscroll-behavior:contain}[data-theme=light] .sf-body{scrollbar-color:rgba(0,0,0,.12) transparent}.sf-body::-webkit-scrollbar{width:4px;height:4px}.sf-body::-webkit-scrollbar-track,.sf-body::-webkit-scrollbar-corner{background:transparent}.sf-body::-webkit-scrollbar-thumb{border-radius:4px;background:#ffffff26}.sf-body::-webkit-scrollbar-thumb:hover{background:#ffffff40}[data-theme=light] .sf-body::-webkit-scrollbar-thumb{background:#0000001f}[data-theme=light] .sf-body::-webkit-scrollbar-thumb:hover{background:#0003}.sf-empty{padding:32px 0;text-align:center;font-family:inherit;font-size:14px;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.sf-field-error{margin:8px 24px 0;font-family:inherit;font-size:12px;line-height:1.5;color:var(--ds-system-red-default, #da0b2e)}.sf-message--error{margin:16px 0 0;padding:12px 16px;background:var(--ds-system-red-low, #ffeeee);color:var(--ds-system-red-high, #d20609);font-family:inherit;font-size:13px;line-height:1.5;border-radius:12px;width:100%;box-sizing:border-box}.sf-footer{display:flex;flex-shrink:0;padding:16px 24px 24px}.sf-footer:empty{display:none}.sf-footer-row{display:flex;gap:8px;width:100%}.sf-footer-row .sf-btn,.sf-footer-row .sf-btn--secondary{flex:1 1 0;min-width:0}.sf-footer>.sf-btn{width:100%}.sf-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;flex:1;height:48px;padding:0 24px;border:none;border-radius:12px;font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer;transition:background-color .15s ease,transform .1s ease}.sf-btn--primary{background:var(--ds-accent-gradient, linear-gradient(196deg, #ff2589 16%, #ff823d 84%));color:#fff}.sf-btn--primary:hover:not(:disabled):not([data-disabled]){filter:brightness(1.05)}.sf-btn--primary:active:not(:disabled):not([data-disabled]){transform:scale(.97)}.sf-btn--primary[data-disabled],.sf-btn--primary:disabled{background:var(--ds-accent-gradient, linear-gradient(196deg, #ff2589 16%, #ff823d 84%));color:#fff;cursor:default;opacity:.5}.sf-btn--secondary{flex:0 0 120px;background:var(--ds-surface-default, #ffffff);border:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));color:var(--ds-content-high, rgba(0, 0, 0, .85))}.sf-token-select{padding:0;display:flex;flex-direction:column}.sf-token-select .sf-search{margin:16px 24px}.sf-token-select .sf-token-list{width:100%;margin-top:0}.sf-search{display:flex;align-items:center;gap:8px;padding:12px 16px;border-radius:12px;border:1.5px solid transparent;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-content-low, rgba(0, 0, 0, .4));box-sizing:border-box;transition:border-color .15s ease}.sf-search:hover{border-color:var(--ds-border-medium, rgba(0, 0, 0, .18))}.sf-search:focus-within{border-color:var(--ds-accent-primary-high, #f0640d)}.sf-search-input{flex:1;min-width:0;border:none;background:transparent;outline:none;font-family:inherit;font-size:16px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.sf-search-input::placeholder{color:var(--ds-content-low, rgba(0, 0, 0, .4))}.sf-token-list{display:flex;flex-direction:column;margin-top:8px}.sf-token-row{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:12px 24px;border:none;background:transparent;cursor:pointer;text-align:left;font-family:inherit;border-radius:0;box-sizing:border-box;transition:background-color .15s ease}.sf-token-row:hover{background:var(--ds-surface-medium, rgba(0, 0, 0, .04))}.sf-token-left{display:flex;align-items:center;gap:16px;min-width:0}.sf-token-icon-wrap{position:relative;width:40px;height:40px;flex-shrink:0}.sf-token-icon{width:40px;height:40px;border-radius:0;object-fit:contain}.sf-token-chain-badge{position:absolute;right:-2px;bottom:-2px;width:16px;height:16px;border-radius:50%;object-fit:cover}.sf-token-name{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap}.sf-token-right{display:flex;flex-direction:column;align-items:flex-end;flex-shrink:0}.sf-token-balance{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-highest, #000000);white-space:nowrap}.sf-token-price{font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));white-space:nowrap}.sf-recipient{display:flex;flex-direction:column;flex:1;min-height:0}.sf-recipient-top{display:flex;flex-direction:column;flex-shrink:0;padding:16px 24px 0}.sf-address-field{display:flex;align-items:center;gap:8px;padding:12px;border-radius:12px;border:1.5px solid transparent;background:var(--ds-surface-medium, #f5f7fa);transition:border-color .15s ease;box-sizing:border-box}.sf-address-field:hover{border-color:var(--ds-border-medium, rgba(0, 0, 0, .18))}.sf-address-field:focus-within{border-color:var(--ds-accent-primary-high, #f0640d)}.sf-address-field[data-filled]{border-color:var(--ds-accent-primary-high, #f0640d)}.sf-address-field[data-invalid]{border-color:var(--ds-system-red-default, #da0b2e)}.sf-address-input{flex:1;min-width:0;border:none;background:transparent;outline:none;resize:none;overflow:hidden;font-family:inherit;font-size:16px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));word-break:break-word;overflow-wrap:anywhere}.sf-address-input::placeholder{color:var(--ds-content-low, rgba(0, 0, 0, .4))}.sf-address-action{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;flex-shrink:0;padding:0;border:none;background:transparent;border-radius:6px;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer;transition:color .15s ease}.sf-address-action:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.sf-address-field[data-filled] .sf-address-action{border-radius:9999px}.sf-tabs{display:flex;gap:0;margin:16px 0 0}.sf-tab{position:relative;padding:8px 12px;border:none;background:transparent;cursor:pointer;font-family:inherit;font-size:16px;font-weight:600;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.sf-tab[data-active]{color:var(--ds-accent-primary-high, #f0640d)}.sf-tab[data-active]:after{content:\\\"\\\";position:absolute;left:0;right:0;bottom:-1px;height:3px;border-radius:9999px;background:var(--ds-accent-primary-high, #f0640d)}.sf-address-list{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;margin-top:8px;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.15) transparent}[data-theme=light] .sf-address-list{scrollbar-color:rgba(0,0,0,.12) transparent}.sf-address-list::-webkit-scrollbar{width:4px}.sf-address-list::-webkit-scrollbar-track{background:transparent}.sf-address-list::-webkit-scrollbar-thumb{border-radius:4px;background:#ffffff26}[data-theme=light] .sf-address-list::-webkit-scrollbar-thumb{background:#0000001f}.sf-address-row{display:flex;align-items:center;gap:16px;width:100%;padding:12px 24px;border:none;background:transparent;cursor:pointer;text-align:left;font-family:inherit;border-radius:0;box-sizing:border-box;transition:background-color .15s ease}.sf-address-row:hover{background:var(--ds-surface-medium, rgba(0, 0, 0, .04))}.sf-address-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;flex-shrink:0;border-radius:50%;overflow:hidden;background:var(--ds-surface-high, #edf1f2);color:var(--ds-content-medium, rgba(0, 0, 0, .65));font-size:14px;font-weight:600}.sf-address-avatar img{position:absolute;inset:0;z-index:1;width:100%;height:100%;object-fit:cover}.sf-address-avatar-fallback{position:relative;z-index:0}.sf-address-meta{display:flex;flex-direction:column;min-width:0}.sf-address-name{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.sf-address-value{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-drawer-content .sf-header{padding:16px}.wi-drawer-content .sf-header .sf-header-btn:last-child{top:16px;right:16px}.wi-drawer-content .sf-token-select{padding:0}.wi-drawer-content .sf-token-select .sf-search{margin:16px}.wi-drawer-content .sf-token-row{padding:12px 16px}.wi-drawer-content .sf-recipient-top{padding:16px 16px 0}.wi-drawer-content .sf-address-row{padding:12px 16px}.wi-drawer-content .sf-footer{padding:12px 16px 24px}.wi-drawer-content .sf-field-error,.wi-drawer-content .sf-message--error{margin:16px 0 0;width:auto}.sf-amount{display:flex;flex-direction:column}.sf-amount-center{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:8px;padding:64px 24px 48px;text-align:center}.sf-amount-usd{font-family:inherit;font-size:14px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));min-height:21px}.sf-amount-input-wrap{display:flex;justify-content:center;max-width:100%}.sf-amount-input{width:auto;max-width:100%;border:none;background:transparent;outline:none;text-align:center;font-family:inherit;font-size:40px;font-weight:700;line-height:1.25;letter-spacing:-.01em;color:var(--ds-content-highest, #000000);field-sizing:content}.sf-amount-input-wrap[data-empty] .sf-amount-input,.sf-amount-input::placeholder{color:var(--ds-content-low, rgba(0, 0, 0, .4))}.sf-amount-input-wrap[data-over] .sf-amount-input{color:var(--ds-system-red-default, #da0b2e)}.sf-amount-balance{display:inline-flex;align-items:center;gap:4px;font-family:inherit;font-size:14px;font-weight:400;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.sf-amount-max{border:none;background:transparent;padding:0;cursor:pointer;font:inherit;color:var(--ds-content-medium, rgba(0, 0, 0, .65));text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.sf-amount-max:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-drawer-content .sf-amount-center{padding:64px 16px 48px}.sf-review{display:flex;flex-direction:column;gap:16px;padding:24px}.sf-review-amount{display:flex;align-items:center;gap:12px;padding:0;background:transparent}.sf-review-amount-icon{width:40px;height:40px;border-radius:0;object-fit:contain;flex-shrink:0}.sf-review-amount-value{display:inline-flex;flex-direction:column;align-items:flex-start;gap:0;font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-highest, #000000);word-break:break-all}.sf-review-amount-symbol{font-size:12px;font-weight:400;line-height:1.5;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.sf-review-parties{position:relative;display:flex;flex-direction:column;gap:0}.sf-review-arrow{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;margin:-4px auto;border-radius:50%;background:var(--ds-surface-default, #ffffff);border:1px solid var(--ds-border-medium, rgba(0, 0, 0, .18));color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.sf-review-card{display:flex;flex-direction:column;gap:8px;padding:16px;border-radius:12px;background:var(--ds-surface-medium, #f5f7fa)}.sf-review-party-label{font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.sf-review-party-head{display:flex;align-items:center;gap:8px}.sf-review-party-avatar{display:inline-flex;width:24px;height:24px;border-radius:50%;overflow:hidden;background:var(--ds-surface-high, #edf1f2);flex-shrink:0}.sf-review-party-avatar img{width:100%;height:100%;object-fit:cover}.sf-review-party-name{font-family:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.sf-review-party-addr{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65));word-break:break-all}.sf-review-section-title{margin:0 0 4px;font-family:inherit;font-size:16px;font-weight:600;letter-spacing:-.01em;color:var(--ds-content-highest, #000000)}.sf-review-row{display:flex;flex-direction:column;align-items:flex-start;gap:4px}.sf-review-row-label{font-family:inherit;font-size:12px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.sf-review-row-value{font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));text-align:left;word-break:break-all}.wi-drawer-content .sf-review{padding:16px}.sf-result{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;padding:48px 24px;text-align:center}.sf-result-icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px}.sf-result-spinner{width:56px;height:56px;border-radius:50%;border:4px solid var(--ds-border-medium, rgba(0, 0, 0, .18));border-top-color:var(--ds-accent-primary-high, #f0640d);animation:sf-spin .8s linear infinite}@keyframes sf-spin{to{transform:rotate(360deg)}}.sf-result-copy{display:flex;flex-direction:column;gap:8px}.sf-result-title{margin:0;font-family:inherit;font-size:18px;font-weight:600;letter-spacing:-.01em;color:var(--ds-content-highest, #000000)}.sf-result-desc{margin:0;font-family:inherit;font-size:14px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.sf-result-hash-card{display:flex;flex-direction:column;gap:8px;width:100%;padding:16px;border-radius:12px;background:var(--ds-surface-medium, #f5f7fa);text-align:left}.sf-result-hash-label{font-family:inherit;font-size:12px;font-weight:500;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.sf-result-hash-row{display:flex;align-items:center;justify-content:space-between;gap:8px}.sf-result-hash-link{border:none;background:transparent;padding:0;cursor:pointer;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;color:var(--ds-accent-primary-high, #f0640d)}.sf-result-copy-btn{display:inline-flex;align-items:center;justify-content:center;min-width:44px;height:28px;padding:0 10px;border:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));border-radius:8px;background:var(--ds-surface-default, #ffffff);cursor:pointer;font-family:inherit;font-size:12px;font-weight:500;color:var(--ds-content-high, rgba(0, 0, 0, .85))}\\n\")","\"use client\";\n\nimport * as React from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { Drawer } from \"vaul\";\nimport { useWalletInfoContext } from \"../providers/wallet-info-provider\";\nimport { WalletIcon } from \"./icons\";\nimport { shortenAddress } from \"../../../lib/format\";\nimport { cn } from \"../../../lib/utils\";\n\nexport interface WalletInfoTriggerProps {\n  asChild?: boolean;\n  className?: string;\n  children?: React.ReactNode;\n}\n\nexport function WalletInfoTrigger({\n  asChild,\n  className,\n  children,\n}: WalletInfoTriggerProps) {\n  const { isMobile, theme, walletAddress } = useWalletInfoContext();\n\n  const hasChildren = children != null;\n  const shouldAsChild = asChild ?? true;\n\n  const triggerContent = hasChildren ? (\n    children\n  ) : (\n    <button\n      type=\"button\"\n      className={cn(\"wi-trigger-btn\", className)}\n      data-theme={theme}\n      data-mobile={isMobile || undefined}\n    >\n      <WalletIcon size={isMobile ? 14 : 16} />\n      <span className=\"wi-trigger-address\">\n        {shortenAddress(walletAddress)}\n      </span>\n    </button>\n  );\n\n  if (isMobile) {\n    return (\n      <Drawer.Trigger asChild={shouldAsChild}>{triggerContent}</Drawer.Trigger>\n    );\n  }\n\n  return (\n    <Popover.Trigger asChild={shouldAsChild}>{triggerContent}</Popover.Trigger>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { Drawer } from \"vaul\";\nimport {\n  useWalletInfoContext,\n  type WalletInfoView,\n} from \"../providers/wallet-info-provider\";\nimport BigNumber from \"bignumber.js\";\nimport { parseNumeric } from \"../../../lib/numeric\";\nimport { useTokenBalance } from \"../queries/use-token-balance\";\nimport {\n  useForgeHoldings,\n  useForgeTokenImages,\n} from \"../queries/use-forge-holdings\";\nimport {\n  CHAIN_IDS,\n  getChainBadgeUrl,\n  getPortraitUrl,\n  isCrossdTokenAddress,\n} from \"../constants\";\nimport { useTokenStats, buildStatsKey } from \"../queries/use-token-stats\";\nimport {\n  formatCrossdAprPercent,\n  useCrossdApr,\n} from \"../queries/use-crossd-apr\";\nimport { useTokenInfo, buildCategoryKey } from \"../queries/use-token-info\";\nimport { useTotalValue } from \"../queries/use-total-value\";\nimport type { Environment } from \"../../../types\";\nimport type { ChainId, TokenBalance } from \"../types\";\nimport type { BridgeToken } from \"../../bridge\";\nimport { QRCodeSVG } from \"qrcode.react\";\nimport {\n  CloseIcon,\n  CopyIcon,\n  CheckIcon,\n  QRIcon,\n  QRLogoIcon,\n  BackIcon,\n  ChevronDownIcon,\n  EyeOpenIcon,\n  EyeClosedIcon,\n  DisconnectIcon,\n  BuyActionIcon,\n  BridgeActionIcon,\n  SendActionIcon,\n  SendSolidIcon,\n  PortfolioActionIcon,\n  ToastCheckIcon,\n} from \"./icons\";\nimport {\n  shortenAddress,\n  formatWei,\n  formatPrice,\n  formatTokenValue,\n} from \"../../../lib/format\";\nimport { cn, isInAppBrowser } from \"../../../lib/utils\";\nimport { WALLET_LABELS } from \"@nexus-cross/crossx-design-system\";\nimport { readSelectedWalletName } from \"../../wallet-connect-modal/last-selected\";\nimport { handleOutlinkClick } from \"../../../lib/outlink\";\nimport {\n  resolveEnvValue,\n  useRemoteConfigValue,\n  type AppsConfig,\n} from \"../../../lib/remote-config\";\nimport { WalletPortfolioBody } from \"../../wallet-portfolio\";\nimport type {\n  SendFailedInfo,\n  SendSubmittedInfo,\n} from \"../../send\";\nimport { BridgeFlow } from \"../../bridge\";\nimport { SendFlow, formatRawTokenAmount } from \"../../send\";\nimport { useTransactionExplorerUrl } from \"../../send/queries/use-chain-info\";\nimport { TokenDetailView } from \"./TokenDetailView\";\nimport { OnePopBody, OnePopActionIcon } from \"../../onepop\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\n\n/**\n * `onBuy`가 reject됐는데 `onBuyDisabledMessage`가 따로 없을 때(=allow 통과 후\n * url/open 단계 실패) 띄우는 기본 안내. on-ramp가 차단된 지역 토스트와 동일 톤.\n */\nconst ONRAMP_BUY_FAILED_MESSAGE = \"On-ramp is not available in your region.\";\n\nfunction getPinnedOrder(\n  env: Environment,\n  chainId: number,\n  symbol: string,\n): number | undefined {\n  const chains = CHAIN_IDS[env];\n  const s = symbol.toUpperCase();\n  if (chainId === chains.cross && s.includes(\"ONEUSD\")) return 0;\n  if (chainId === chains.cross && s === \"ONE\") return 1;\n  if (chainId === chains.bsc && s === \"ONE\") return 2;\n  return undefined;\n}\n\nfunction parseChainIdNum(caip2: ChainId): number {\n  return parseInt(caip2.split(\":\")[1], 10);\n}\n\n/**\n * 현재 페이지가 gametoken 사이트 안인지 판별.\n * apps.json(gametokenSites)의 URL prefix 하위에서만 브릿지 버튼이\n * 같은 창으로 이동하고, 그 외에는 모두 새창으로 연다.\n */\nfunction isWithinGametokenSite(sites: string[] | undefined): boolean {\n  if (typeof window === \"undefined\" || !sites?.length) return false;\n  const { origin, pathname } = window.location;\n  const current = origin + pathname;\n  return sites.some(\n    (site) => current === site || current.startsWith(`${site}/`),\n  );\n}\n\n/** \"127,475.34\" → { main: \"127,475\", decimal: \".34\" } */\nfunction splitAmount(value: string): { main: string; decimal: string } {\n  const dotIdx = value.lastIndexOf(\".\");\n  if (dotIdx === -1) return { main: value, decimal: \"\" };\n  return { main: value.slice(0, dotIdx), decimal: value.slice(dotIdx) };\n}\n\n/** 주소 기반 간단한 해시로 그라디언트 아바타 색을 결정. */\nfunction getAvatarGradient(address: string): string {\n  const hex = address.toLowerCase().replace(/^0x/, \"\");\n  const seed = hex.length >= 8 ? hex.slice(0, 8) : hex.padEnd(8, \"0\");\n  const h1 = parseInt(seed.slice(0, 4), 16) % 360;\n  const h2 = (h1 + 60 + (parseInt(seed.slice(4, 8), 16) % 120)) % 360;\n  return `linear-gradient(135deg, hsl(${h1} 85% 65%), hsl(${h2} 80% 55%))`;\n}\n\n/**\n * 여러 폰트 크기(통화기호/정수부/소수부)를 가진 값이 컨테이너 폭을 넘으면\n * 줄바꿈 대신 전체를 같은 비율로 축소하는 배율(0~1)을 계산한다. 배율은\n * `--wi-total-scale` CSS 변수로 내려 각 span 의 font-size(`calc(px * var)`)에\n * 곱해진다. 자연 폭은 clone 을 절대배치로 붙여(레이아웃 미영향, 폰트 상속 유지)\n * scale=1 기준으로 측정하므로 clamp/진동이 없다.\n */\nfunction useFitScale(dep: string, minScale = 0.5) {\n  const containerRef = React.useRef<HTMLDivElement | null>(null);\n  const valueRef = React.useRef<HTMLDivElement | null>(null);\n  const [scale, setScale] = React.useState(1);\n\n  React.useLayoutEffect(() => {\n    const container = containerRef.current;\n    const el = valueRef.current;\n    if (!container || !el) return;\n\n    const measure = () => {\n      const available = container.clientWidth;\n      if (!available) return;\n\n      const clone = el.cloneNode(true) as HTMLElement;\n      clone.style.setProperty(\"--wi-total-scale\", \"1\");\n      clone.style.position = \"absolute\";\n      clone.style.left = \"-9999px\";\n      clone.style.top = \"-9999px\";\n      clone.style.visibility = \"hidden\";\n      clone.style.maxWidth = \"none\";\n      clone.style.width = \"max-content\";\n      container.appendChild(clone);\n      const natural = clone.offsetWidth;\n      container.removeChild(clone);\n\n      const next =\n        natural > available ? Math.max(minScale, available / natural) : 1;\n      setScale(next);\n    };\n\n    measure();\n    const ro = new ResizeObserver(measure);\n    ro.observe(container);\n    return () => ro.disconnect();\n  }, [dep, minScale]);\n\n  return { containerRef, valueRef, scale };\n}\n\nexport interface WalletInfoContentProps {\n  align?: \"start\" | \"center\" | \"end\";\n  sideOffset?: number;\n  className?: string;\n  children?: React.ReactNode;\n}\n\nexport function WalletInfoContent({\n  align = \"end\",\n  sideOffset = 12,\n  className,\n  children,\n}: WalletInfoContentProps) {\n  const {\n    env,\n    theme,\n    isMobile,\n    open,\n    drawerDirection,\n    modal,\n    showBalance,\n    showForgeToken,\n    showGameToken,\n    showQR,\n    showBridge,\n    qrLogoSrc,\n    walletAddress,\n    accountName,\n    sendAccounts,\n    profileImageUrl,\n    connectorName,\n    preferredTokens,\n    close,\n    onSelectWallet,\n    onCopyAddress,\n    onBuy,\n    onDisconnect,\n    disconnectLabel,\n    termsUrl,\n    termsLabel,\n    privacyUrl,\n    privacyLabel,\n    portalStyle,\n    navContent,\n    navPosition,\n    footerContent,\n    view,\n    setView: setViewFromCtx,\n    selectedSendToken,\n    setSelectedSendToken,\n    selectedDetailToken,\n    setSelectedDetailToken,\n    showPortfolio,\n    showOnePop,\n    onePopSummary,\n    onOnePopOpen,\n    onOnePopSend,\n    onOnePopClaim,\n    onOnePopActivity,\n    onOnePopRepliesSeen,\n    onePopLogoSrc,\n    onePopGetTokenSlot,\n    onePopHeroSrc,\n    portfolioTitle,\n    portfolioSections,\n    showTotalAssets,\n    totalAssetsLabel,\n    sendTransaction,\n    getTransactionReceipt,\n    estimateGas,\n    onOutlink,\n    lpBalanceReader,\n    stakingRewardsReader,\n    bridgeTokens,\n    bridgeHistory,\n    getBridgeQuote,\n    getBridgeToTokens,\n    getBridgeApproval,\n    approveBridge,\n    submitBridge,\n    onReceive,\n    onSend,\n    onBuyDisabledMessage,\n    onBridgeDisabledMessage,\n    onSendDisabledMessage,\n  } = useWalletInfoContext();\n\n  // 호출측이 이름을 아무것도 안 넘긴 경우의 자체 감지 폴백 — 커넥트 모달에서\n  // 사용자가 고른 지갑(모달에 표기됐던 라벨 그대로), 없으면 ONEpocket 인앱\n  // 브라우저 UA. 둘 다 브라우저에서만 읽을 수 있어 effect 로 채워 SSR hydration\n  // mismatch 를 피한다.\n  const [detectedWalletName, setDetectedWalletName] = React.useState<string>();\n  React.useEffect(() => {\n    setDetectedWalletName(\n      readSelectedWalletName() ??\n        (isInAppBrowser() ? WALLET_LABELS.default : undefined),\n    );\n  }, []);\n\n  // 계정 라벨 표시용 폴백 체인. 계정 이름 개념이 없는 커넥터(ONEpocket 앱 /\n  // 익스텐션 / MetaMask 등 외부 지갑)는 `accountName`이 비어 오는데, 그대로\n  // 번호가 붙은 이름으로 떨어지면 실제 1번 계정과 구분이 안 된다. 순서:\n  //   1. accountName          — 호출측이 준 계정 라벨\n  //   2. connectorName        — 호출측이 준 지갑 브랜드명(connect-kit 이 주입).\n  //                             WalletInfo 루트에서 CONNECTOR_REGISTRY[connectorId]\n  //                             로도 해석된다\n  //   3. detectedWalletName   — 커넥트 모달 선택 기록 / 인앱 브라우저 UA\n  //   4. \"Account\"            — 지갑을 특정할 수 없을 때. 번호를 붙이지 않는다 —\n  //                             \"Account 1\" 은 여러 계정 중 1번이라는 뜻으로\n  //                             읽혀서 실제 계정 목록과 충돌한다\n  const displayAccountName =\n    accountName ?? connectorName ?? detectedWalletName ?? \"Account\";\n\n  // Buy 카드용 ad-hoc 토스트. on-ramp가 차단된 국가 등 시각적으로는 비활성이지만\n  // 클릭 시 사용자에게 안내 메시지를 보여줘야 할 때 사용. 4초 후 자동 사라짐.\n  const [actionToast, setActionToast] = React.useState<string | null>(null);\n  const actionToastTimerRef =\n    React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  const showActionToast = React.useCallback((message: string) => {\n    setActionToast(message);\n    clearTimeout(actionToastTimerRef.current);\n    actionToastTimerRef.current = setTimeout(() => setActionToast(null), 4000);\n  }, []);\n  React.useEffect(\n    () => () => clearTimeout(actionToastTimerRef.current),\n    [],\n  );\n\n  const buyDisabledByMessage = !!onBuyDisabledMessage;\n  const handleBuyClick = React.useCallback(() => {\n    if (buyDisabledByMessage) {\n      showActionToast(onBuyDisabledMessage!);\n      return;\n    }\n    // onBuy가 Promise를 반환하면(connect-kit의 on-ramp 자동 wiring 등) reject 시\n    // allow/url API 실패를 사용자에게 안내한다. `onBuyDisabledMessage`가 없을 때만\n    // 도달하므로(allow 통과 케이스), 동일 톤의 기본 안내 메시지를 토스트로 재사용한다.\n    const result = onBuy?.();\n    if (result && typeof (result as Promise<void>).then === \"function\") {\n      void (result as Promise<void>).catch(() => {\n        showActionToast(onBuyDisabledMessage ?? ONRAMP_BUY_FAILED_MESSAGE);\n      });\n    }\n  }, [buyDisabledByMessage, onBuyDisabledMessage, onBuy, showActionToast]);\n\n  // Bridge / Send 게이트. 연결된 지갑이 CROSSx 계열이 아닐 때 react 레이어가\n  // 안내 메시지를 주입한다(Buy 비활성 패턴과 동일). 메시지가 있으면 해당 액션은\n  // 시각적으로 비활성 처리되고, 클릭 시 토스트만 띄운 뒤 실제 동작은 막힌다.\n  const bridgeDisabledByMessage = !!onBridgeDisabledMessage;\n  const sendDisabledByMessage = !!onSendDisabledMessage;\n\n  const { tokens: rawTokens, isLoading } = useTokenBalance(\n    env,\n    walletAddress,\n    open && showBalance,\n  );\n  const { statsMap } = useTokenStats(env, showBalance);\n  const { aprPercent: crossdAprPercent } = useCrossdApr();\n  const { categoryMap, infoMap } = useTokenInfo(env, showBalance);\n  const showTotal = showBalance && showTotalAssets;\n  const {\n    displayValue: totalDisplay,\n    isLowerBound: totalIsLowerBound,\n    isLoading: totalLoading,\n  } = useTotalValue(env, walletAddress, open && showTotal);\n  const [totalHidden, setTotalHidden] = React.useState(false);\n  const footerRef = React.useRef<HTMLDivElement>(null);\n  const [footerHeight, setFooterHeight] = React.useState(0);\n  const { holdings: forgeHoldings, isLoading: isForgeLoading } =\n    useForgeHoldings(env, walletAddress, open && showBalance && showForgeToken);\n\n  const forgeTokenAddresses = React.useMemo(\n    () => forgeHoldings.map((h) => h.token),\n    [forgeHoldings],\n  );\n  const { imageMap: forgeImageMap } = useForgeTokenImages(\n    env,\n    forgeTokenAddresses,\n    showForgeToken && forgeHoldings.length > 0,\n  );\n\n  const tokens = React.useMemo(() => {\n    const pinned: TokenBalance[] = [];\n    const preferred: TokenBalance[] = [];\n    const rest: TokenBalance[] = [];\n\n    const prefKeyMap = new Map(\n      (preferredTokens ?? []).map((t, i) => [\n        `${parseChainIdNum(t.chainId)}:${t.address.toLowerCase()}`,\n        i,\n      ]),\n    );\n\n    for (const token of rawTokens) {\n      const isPinned =\n        getPinnedOrder(env, token.chainId, token.symbol) !== undefined;\n      const tokenKey = `${token.chainId}:${token.address.toLowerCase()}`;\n      const prefIdx = prefKeyMap.get(tokenKey);\n      const balance = (\n        parseNumeric(token.quantity.numeric) ?? new BigNumber(0)\n      ).shiftedBy(-token.quantity.decimals);\n\n      if (isPinned) {\n        pinned.push(token);\n      } else if (prefIdx !== undefined) {\n        preferred.push(token);\n      } else if (balance.isGreaterThan(0)) {\n        if (!showGameToken) {\n          const cat = categoryMap.get(\n            buildCategoryKey(token.chainId, token.address),\n          );\n          if (cat === \"game\") continue;\n        }\n        rest.push(token);\n      }\n    }\n\n    pinned.sort((a, b) => {\n      const aIdx = getPinnedOrder(env, a.chainId, a.symbol) ?? 99;\n      const bIdx = getPinnedOrder(env, b.chainId, b.symbol) ?? 99;\n      return aIdx - bIdx;\n    });\n\n    preferred.sort((a, b) => {\n      const aIdx =\n        prefKeyMap.get(`${a.chainId}:${a.address.toLowerCase()}`) ?? 99;\n      const bIdx =\n        prefKeyMap.get(`${b.chainId}:${b.address.toLowerCase()}`) ?? 99;\n      return aIdx - bIdx;\n    });\n\n    rest.sort((a, b) => {\n      const statA = statsMap.get(buildStatsKey(a.chainId, a.address));\n      const statB = statsMap.get(buildStatsKey(b.chainId, b.address));\n      const valueA = (parseNumeric(a.quantity.numeric) ?? new BigNumber(0))\n        .shiftedBy(-a.quantity.decimals)\n        .multipliedBy(parseNumeric(statA?.price) ?? 0);\n      const valueB = (parseNumeric(b.quantity.numeric) ?? new BigNumber(0))\n        .shiftedBy(-b.quantity.decimals)\n        .multipliedBy(parseNumeric(statB?.price) ?? 0);\n      return valueB.minus(valueA).toNumber();\n    });\n\n    return [...pinned, ...preferred, ...rest];\n  }, [rawTokens, env, statsMap, showGameToken, categoryMap, preferredTokens]);\n\n  const sortedForgeHoldings = React.useMemo(() => {\n    if (!showForgeToken) return [];\n    const balanceAddressSet = new Set(\n      rawTokens.map((t) => t.address.toLowerCase()),\n    );\n    return [...forgeHoldings]\n      .filter((h) => !balanceAddressSet.has(h.token.toLowerCase()))\n      .sort((a, b) => {\n        const valueA = parseNumeric(a.value) ?? new BigNumber(0);\n        const valueB = parseNumeric(b.value) ?? new BigNumber(0);\n        return valueB.minus(valueA).toNumber();\n      });\n  }, [forgeHoldings, showForgeToken, rawTokens]);\n\n  const hasTransitioned = React.useRef(false);\n  const [sendToast, setSendToast] = React.useState<{\n    status: \"pending\" | \"success\" | \"error\";\n    txHash: `0x${string}`;\n    chainId: number;\n    description: string;\n  } | null>(null);\n  const sendToastTimerRef =\n    React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  const toastChainId =\n    sendToast?.chainId ?? selectedSendToken?.chainId ?? tokens[0]?.chainId ?? 0;\n  const { transactionUrl: sendToastUrl } = useTransactionExplorerUrl(\n    env,\n    toastChainId,\n    sendToast?.txHash,\n    Boolean(sendToast?.txHash && toastChainId),\n  );\n\n  const setView = React.useCallback(\n    (v: WalletInfoView) => {\n      hasTransitioned.current = true;\n      setViewFromCtx(v);\n    },\n    [setViewFromCtx],\n  );\n  const [sendInitialStep, setSendInitialStep] =\n    React.useState<\"token\" | \"recipient\">(\"recipient\");\n\n  const handleBackToWallet = React.useCallback(() => {\n    setSelectedSendToken(null);\n    setSendInitialStep(\"recipient\");\n    setView(\"wallet\");\n  }, [setSelectedSendToken, setView]);\n\n  const handleSelectSendToken = React.useCallback(\n    (token: TokenBalance, initialStep: \"token\" | \"recipient\" = \"recipient\") => {\n      // 토큰 행 탭도 전송 진입점이므로 동일 게이트를 적용한다.\n      if (sendDisabledByMessage) {\n        showActionToast(onSendDisabledMessage!);\n        return;\n      }\n      setSendInitialStep(initialStep);\n      setSelectedSendToken(token);\n      setView(\"send\");\n    },\n    [\n      sendDisabledByMessage,\n      onSendDisabledMessage,\n      showActionToast,\n      setSelectedSendToken,\n      setView,\n    ],\n  );\n\n  // 토큰 행 탭 → 해당 토큰의 상세/이력 화면. (기존엔 바로 전송으로 진입했다.)\n  const handleSelectTokenDetail = React.useCallback(\n    (token: TokenBalance) => {\n      setSelectedDetailToken(token);\n      setView(\"token-detail\");\n    },\n    [setSelectedDetailToken, setView],\n  );\n\n  // ONEpop 진입. 조회(SIWE 서명 필요)는 여기서 처음 트리거된다 — 지갑을 열기만\n  // 해도 서명 팝업이 뜨는 일이 없도록 주입측이 이 시점까지 미룰 수 있게 한다.\n  const handleOnePopOpen = React.useCallback(() => {\n    onOnePopOpen?.();\n    setView(\"onepop\");\n  }, [onOnePopOpen, setView]);\n\n  // 상세화면 → 지갑으로 복귀. 선택된 상세 토큰을 정리한다.\n  const handleBackFromDetail = React.useCallback(() => {\n    setSelectedDetailToken(null);\n    setView(\"wallet\");\n  }, [setSelectedDetailToken, setView]);\n\n  // 상세화면 안의 Send 버튼: 기존 전송 플로우를 recipient 스텝으로 진입한다.\n  const handleSendFromDetail = React.useCallback(\n    (token: TokenBalance) => {\n      handleSelectSendToken(token, \"recipient\");\n    },\n    [handleSelectSendToken],\n  );\n\n  // 브릿지 버튼 이동 대상 — apps.json(gametokenBridge)이 단일 소스.\n  // 로드 전/실패 시 undefined이며 그동안 버튼 클릭은 no-op.\n  const remoteBridgeWebUrl = useRemoteConfigValue<AppsConfig, string>(\n    \"apps.json\",\n    env,\n    (config) => resolveEnvValue(config?.gametokenBridge, env),\n  );\n\n  // 브릿지 버튼을 같은 창으로 여는 gametoken 사이트 URL prefix 목록.\n  const remoteGametokenSites = useRemoteConfigValue<AppsConfig, string[]>(\n    \"apps.json\",\n    env,\n    (config) => config?.gametokenSites,\n  );\n\n  // onBridge 콜백/임베디드 BridgeFlow(setView(\"bridge\"))를 무시하고 항상\n  // gametoken 브릿지 웹으로 이동한다 — apps.json(gametokenBridge)이 단일 소스.\n  // 단, gametoken 사이트(apps.json gametokenSites) 안이면 새창 대신 같은 창 이동.\n  const handleBridgeClick = React.useCallback(() => {\n    if (bridgeDisabledByMessage) {\n      showActionToast(onBridgeDisabledMessage!);\n      return;\n    }\n    if (!remoteBridgeWebUrl) return;\n    if (isWithinGametokenSite(remoteGametokenSites)) {\n      window.location.assign(remoteBridgeWebUrl);\n      return;\n    }\n    window.open(remoteBridgeWebUrl, \"_blank\", \"noopener,noreferrer\");\n  }, [\n    bridgeDisabledByMessage,\n    onBridgeDisabledMessage,\n    remoteBridgeWebUrl,\n    remoteGametokenSites,\n    showActionToast,\n  ]);\n\n  const handleSendSubmitted = React.useCallback((info: SendSubmittedInfo) => {\n    clearTimeout(sendToastTimerRef.current);\n    setSelectedSendToken(null);\n    setView(\"wallet\");\n    setSendToast({\n      status: \"pending\",\n      txHash: info.txHash,\n      chainId: info.chainId,\n      description: `${info.amount} ${info.tokenSymbol} to ${shortenAddress(info.recipient)}`,\n    });\n  }, [setSelectedSendToken, setView]);\n\n  const handleConfirmSendSuccess = React.useCallback((txHash?: `0x${string}`) => {\n    const chainId = selectedSendToken?.chainId;\n    setSelectedSendToken(null);\n    setView(\"wallet\");\n    if (!txHash || !chainId) return;\n    setSendToast((current) => ({\n      status: \"success\",\n      txHash,\n      chainId,\n      description: current?.txHash === txHash\n        ? current.description\n        : \"Tap to see your transaction\",\n    }));\n    clearTimeout(sendToastTimerRef.current);\n    sendToastTimerRef.current = setTimeout(() => setSendToast(null), 5000);\n  }, [selectedSendToken?.chainId, setSelectedSendToken, setView]);\n\n  const handleSendFailed = React.useCallback((info: SendFailedInfo) => {\n    const txHash = info.txHash;\n    if (!txHash) return;\n    setSendToast({\n      status: \"error\",\n      txHash,\n      chainId: info.chainId,\n      description: info.message,\n    });\n    clearTimeout(sendToastTimerRef.current);\n    sendToastTimerRef.current = setTimeout(() => setSendToast(null), 5000);\n  }, []);\n\n  // QR 버튼 동작: 외부 onReceive prop 우선, 미주입 + showQR=true면\n  // 내장 QR view로 전환.\n  const effectiveReceive = React.useMemo<(() => void) | undefined>(() => {\n    if (onReceive) return onReceive;\n    if (showQR) return () => setView(\"qr\");\n    return undefined;\n  }, [onReceive, showQR, setView]);\n\n  // Send 버튼 동작: 외부 onSend prop 우선, 미주입이면 토큰 선택 스텝으로\n  // 진입 (토큰을 먼저 고른 뒤 수신자 → 금액 순으로 진행).\n  const effectiveSend = React.useMemo<(() => void) | undefined>(() => {\n    if (onSend) return onSend;\n    if (tokens.length === 0) return undefined; // 로딩 중/빈 리스트면 비활성\n    const first = tokens[0];\n    return () => handleSelectSendToken(first, \"token\");\n  }, [onSend, tokens, handleSelectSendToken]);\n\n  // Send 액션 카드 클릭. 게이트가 걸려 있으면 토스트만 띄우고 실제 전송 진입은\n  // 막는다(`effectiveSend`가 외부 onSend를 그대로 반환하는 경우도 포함).\n  const handleSendClick = React.useCallback(() => {\n    if (sendDisabledByMessage) {\n      showActionToast(onSendDisabledMessage!);\n      return;\n    }\n    effectiveSend?.();\n  }, [sendDisabledByMessage, onSendDisabledMessage, effectiveSend, showActionToast]);\n\n  React.useEffect(() => {\n    if (!open) {\n      hasTransitioned.current = false;\n      setSendInitialStep(\"recipient\");\n    }\n  }, [open]);\n\n  const scrollRef = React.useRef<HTMLDivElement>(null);\n\n  React.useEffect(() => {\n    const el = scrollRef.current;\n    if (!el || !isMobile) return;\n    const stop = (e: PointerEvent) => {\n      if (e.pointerType === \"touch\") e.stopPropagation();\n    };\n    el.addEventListener(\"pointerdown\", stop);\n    return () => el.removeEventListener(\"pointerdown\", stop);\n  }, [isMobile]);\n\n  const [copied, setCopied] = React.useState(false);\n  const copyTimerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  const [accountModalOpen, setAccountModalOpen] = React.useState(false);\n\n  React.useEffect(() => {\n    if (!accountModalOpen) return;\n    const handleClose = () => setAccountModalOpen(false);\n    const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setAccountModalOpen(false); };\n    const timer = setTimeout(() => {\n      // capture:true → stopPropagation 으로 막힌 이벤트도 잡음\n      document.addEventListener('mousedown', handleClose, true);\n      document.addEventListener('keydown', handleKey, true);\n    }, 0);\n    return () => {\n      clearTimeout(timer);\n      document.removeEventListener('mousedown', handleClose, true);\n      document.removeEventListener('keydown', handleKey, true);\n    };\n  }, [accountModalOpen]);\n\n  // Bridge 모드에서는 네이티브 지갑 선택기가 웹 컨텍스트 밖에 떠서\n  // 위의 mousedown/ESC 리스너가 트리거되지 않는다. 지갑이 실제로 바뀌면\n  // (walletAddress 변경) chevron(accountModalOpen)을 닫아 ^ 가 정상 복귀하도록 한다.\n  React.useEffect(() => {\n    setAccountModalOpen(false);\n  }, [walletAddress]);\n\n  React.useEffect(() => {\n    return () => clearTimeout(copyTimerRef.current);\n  }, []);\n\n  React.useEffect(() => {\n    return () => clearTimeout(sendToastTimerRef.current);\n  }, []);\n\n  React.useEffect(() => {\n    const footerEl = footerRef.current;\n    if (!footerEl || footerContent === null) {\n      setFooterHeight(0);\n      return;\n    }\n\n    const updateFooterHeight = () => {\n      setFooterHeight(footerEl.offsetHeight);\n    };\n\n    updateFooterHeight();\n\n    if (typeof ResizeObserver === \"undefined\") return;\n\n    const observer = new ResizeObserver(updateFooterHeight);\n    observer.observe(footerEl);\n    return () => observer.disconnect();\n  }, [footerContent]);\n\n  const handleCopyAddress = React.useCallback(async () => {\n    if (copied) return;\n    try {\n      await navigator.clipboard.writeText(walletAddress);\n      setCopied(true);\n      clearTimeout(copyTimerRef.current);\n      copyTimerRef.current = setTimeout(() => setCopied(false), 2000);\n      onCopyAddress?.(walletAddress, true);\n    } catch {\n      onCopyAddress?.(walletAddress, false);\n    }\n  }, [walletAddress, onCopyAddress, copied]);\n\n  const handleImageLoad = React.useCallback(\n    (e: React.SyntheticEvent<HTMLImageElement>) => {\n      e.currentTarget.setAttribute(\"data-loaded\", \"true\");\n    },\n    [],\n  );\n\n  const avatarStyle = React.useMemo<React.CSSProperties>(\n    () => ({ background: getAvatarGradient(walletAddress) }),\n    [walletAddress],\n  );\n\n  const resolvedProfileUrl = React.useMemo(() => {\n    if (profileImageUrl) return profileImageUrl;\n    const bg = theme === \"dark\" ? \"1A1A2E\" : \"F3F6F8\";\n    return getPortraitUrl(env, walletAddress, { size: 120, bg });\n  }, [profileImageUrl, env, walletAddress, theme]);\n\n  const totalSplit = React.useMemo(\n    () => splitAmount(totalDisplay),\n    [totalDisplay],\n  );\n\n  // 총자산 금액이 길어 폭을 넘으면 줄바꿈 대신 폰트를 비율로 축소한다.\n  const {\n    containerRef: totalBodyRef,\n    valueRef: totalValueRef,\n    scale: totalScale,\n  } = useFitScale(totalHidden ? \"••••••\" : totalDisplay);\n\n  const legalLinks =\n    termsUrl || privacyUrl ? (\n      <div className=\"wi-legal-links\">\n        {termsUrl && (\n          <a\n            className=\"wi-legal-link\"\n            href={termsUrl}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            data-track=\"terms_link\"\n            onClick={(e) =>\n              handleOutlinkClick(\n                e,\n                termsUrl,\n                { category: \"terms\", origin: \"terms\" },\n                onOutlink,\n              )\n            }\n          >\n            {termsLabel}\n          </a>\n        )}\n        {termsUrl && privacyUrl && (\n          <span className=\"wi-legal-sep\" aria-hidden>\n            ·\n          </span>\n        )}\n        {privacyUrl && (\n          <a\n            className=\"wi-legal-link\"\n            href={privacyUrl}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            data-track=\"privacy_link\"\n            onClick={(e) =>\n              handleOutlinkClick(\n                e,\n                privacyUrl,\n                { category: \"privacy\", origin: \"privacy\" },\n                onOutlink,\n              )\n            }\n          >\n            {privacyLabel}\n          </a>\n        )}\n      </div>\n    ) : null;\n\n  const defaultDisconnectButton = onDisconnect ? (\n    <button\n      type=\"button\"\n      className=\"wi-disconnect-btn\"\n      data-track=\"disconnect\"\n      onClick={() => {\n        onDisconnect();\n        close();\n      }}\n    >\n      <DisconnectIcon size={16} />\n      <span className=\"wi-disconnect-label\">{disconnectLabel}</span>\n    </button>\n  ) : null;\n  const showDefaultQuickActions = showBalance && !navContent;\n\n  // Figma 2.4.0: Send 는 액션 행을 떠나 총자산 금액 우측 아이콘 버튼이 되고,\n  // 그 슬롯을 ONEpop 이 차지한다. 다만 총자산 섹션이 꺼져 있으면(showTotalAssets\n  // =false) 아이콘 버튼이 렌더될 자리가 없으므로, 그 경우에만 Send 카드를\n  // 액션 행에 되돌려 진입점이 사라지지 않게 한다.\n  const sendInActionRow = !showTotal;\n\n  const sendActionButton = (\n    <button\n      type=\"button\"\n      className={cn(\n        \"wi-action-card\",\n        sendDisabledByMessage && \"wi-action-card--dimmed\",\n      )}\n      onClick={handleSendClick}\n      disabled={!sendDisabledByMessage && !effectiveSend}\n      aria-disabled={sendDisabledByMessage || undefined}\n      aria-label=\"Send\"\n      data-track=\"send_open\"\n    >\n      <SendActionIcon size={20} />\n      <span>Send</span>\n    </button>\n  );\n\n  const defaultQuickActions = showDefaultQuickActions ? (\n    <div className=\"wi-action-row\" aria-label=\"Wallet actions\">\n      <button\n        type=\"button\"\n        className={cn(\n          \"wi-action-card\",\n          buyDisabledByMessage && \"wi-action-card--dimmed\",\n        )}\n        onClick={handleBuyClick}\n        disabled={!onBuy && !buyDisabledByMessage}\n        aria-disabled={buyDisabledByMessage || undefined}\n        aria-label=\"Buy\"\n        data-track=\"buy\"\n      >\n        <BuyActionIcon size={20} />\n        <span>Buy</span>\n      </button>\n      {showBridge ? (\n        <button\n          type=\"button\"\n          className={cn(\n            \"wi-action-card\",\n            bridgeDisabledByMessage && \"wi-action-card--dimmed\",\n          )}\n          onClick={handleBridgeClick}\n          aria-disabled={bridgeDisabledByMessage || undefined}\n          aria-label=\"Bridge\"\n          data-track=\"bridge_open\"\n        >\n          <BridgeActionIcon size={20} />\n          <span>Bridge</span>\n        </button>\n      ) : (\n        <button\n          type=\"button\"\n          className=\"wi-action-card\"\n          onClick={effectiveReceive}\n          disabled={!effectiveReceive}\n          aria-label=\"Receive\"\n          data-track=\"receive\"\n        >\n          <QRIcon size={20} />\n          <span>Receive</span>\n        </button>\n      )}\n      {sendInActionRow && sendActionButton}\n      {showOnePop && (\n        <button\n          type=\"button\"\n          className=\"wi-action-card\"\n          onClick={handleOnePopOpen}\n          aria-label=\"Open ONEpop\"\n          data-track=\"onepop_open\"\n        >\n          <OnePopActionIcon size={20} />\n          <span>ONEpop</span>\n        </button>\n      )}\n      {showPortfolio ? (\n        <button\n          type=\"button\"\n          className=\"wi-action-card\"\n          onClick={() => setView(\"portfolio\")}\n          aria-label=\"View portfolio\"\n          data-track=\"portfolio_open\"\n        >\n          <PortfolioActionIcon size={20} />\n          <span>Portfolio</span>\n        </button>\n      ) : showBridge ? (\n        // Portfolio가 꺼져 있으면 이 슬롯을 Receive(QR)로 대체한다. Bridge가\n        // slot 2를 차지할 때만 노출해 Receive 카드가 중복되지 않도록 한다.\n        <button\n          type=\"button\"\n          className=\"wi-action-card\"\n          onClick={effectiveReceive}\n          disabled={!effectiveReceive}\n          aria-label=\"Receive\"\n          data-track=\"receive\"\n        >\n          <QRIcon size={20} />\n          <span>Receive</span>\n        </button>\n      ) : null}\n    </div>\n  ) : null;\n\n  const walletView = (\n    <div\n      key=\"wallet\"\n      className={cn(\n        \"wi-view-inner\",\n        hasTransitioned.current && \"wi-view-wallet\",\n      )}\n    >\n      {showTotal && (\n        <div className=\"wi-total-section\">\n          <div className=\"wi-total-head\">\n            <span className=\"wi-total-label\">{totalAssetsLabel}</span>\n            <span\n              role=\"button\"\n              tabIndex={0}\n              className=\"wi-total-eye\"\n              onClick={(e) => {\n                e.stopPropagation();\n                setTotalHidden((prev) => !prev);\n              }}\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\" || e.key === \" \") {\n                  e.preventDefault();\n                  e.stopPropagation();\n                  setTotalHidden((prev) => !prev);\n                }\n              }}\n              aria-label={totalHidden ? \"Show balance\" : \"Hide balance\"}\n              data-track=\"balance_toggle\"\n            >\n              {totalHidden ? <EyeClosedIcon /> : <EyeOpenIcon />}\n            </span>\n          </div>\n          <div className=\"wi-total-body\">\n            {/* 폭 측정 컨테이너는 금액만 담는다 — Send 버튼이 같은 컨테이너에\n                있으면 available 폭이 과대 계산돼 축소 배율이 어긋난다. */}\n            <div className=\"wi-total-value-wrap\" ref={totalBodyRef}>\n              {totalLoading ? (\n                <div\n                  className=\"wi-skeleton-block wi-total-skeleton\"\n                  style={{ width: 160, height: 42 }}\n                />\n              ) : (\n                <div\n                  className=\"wi-total-value\"\n                  data-hidden={totalHidden || undefined}\n                  ref={totalValueRef}\n                  style={\n                    { \"--wi-total-scale\": totalScale } as React.CSSProperties\n                  }\n                >\n                  {totalHidden ? (\n                    <span className=\"wi-total-mask\">••••••</span>\n                  ) : (\n                    <>\n                      {totalIsLowerBound && (\n                        <span className=\"wi-total-lte\">≤</span>\n                      )}\n                      <span className=\"wi-total-currency\">$</span>\n                      <span className=\"wi-total-amount\">\n                        {totalSplit.main}\n                      </span>\n                      {totalSplit.decimal && (\n                        <span className=\"wi-total-decimal\">\n                          {totalSplit.decimal}\n                        </span>\n                      )}\n                    </>\n                  )}\n                </div>\n              )}\n            </div>\n            {/* Figma 2.4.0: Send 진입점이 액션 행에서 이 자리로 옮겨왔다. */}\n            <button\n              type=\"button\"\n              className={cn(\n                \"wi-total-send\",\n                sendDisabledByMessage && \"wi-total-send--dimmed\",\n              )}\n              onClick={handleSendClick}\n              disabled={!sendDisabledByMessage && !effectiveSend}\n              aria-disabled={sendDisabledByMessage || undefined}\n              aria-label=\"Send\"\n              data-track=\"send_open\"\n            >\n              <SendSolidIcon size={27} />\n            </button>\n          </div>\n        </div>\n      )}\n\n      {navPosition === \"top\" && (\n        navContent ? (\n          <div className=\"wi-nav-fixed\">{navContent}</div>\n        ) : (\n          defaultQuickActions\n        )\n      )}\n\n      {showBalance && (\n        <div ref={scrollRef} className=\"wi-scrollable-body\" data-vaul-no-drag>\n          <div className=\"wi-token-list\">\n            {isLoading &&\n              Array.from({ length: 5 }, (_, i) => (\n                <div key={i} className=\"wi-skeleton-item\">\n                  <div className=\"wi-skeleton-left\">\n                    <div className=\"wi-skeleton-block wi-skeleton-icon\" />\n                    <div className=\"wi-skeleton-lines\">\n                      <div\n                        className=\"wi-skeleton-block\"\n                        style={{ width: 64, height: 14 }}\n                      />\n                      <div\n                        className=\"wi-skeleton-block\"\n                        style={{ width: 80, height: 12 }}\n                      />\n                    </div>\n                  </div>\n                  <div className=\"wi-skeleton-right\">\n                    <div\n                      className=\"wi-skeleton-block\"\n                      style={{ width: 72, height: 14 }}\n                    />\n                    <div\n                      className=\"wi-skeleton-block\"\n                      style={{ width: 48, height: 12 }}\n                    />\n                  </div>\n                </div>\n              ))}\n\n            {!isLoading && tokens.length === 0 && !showForgeToken && (\n              <div className=\"wi-empty\">No Tokens Found</div>\n            )}\n\n            {tokens.map((token) => {\n              const stat = statsMap.get(\n                buildStatsKey(token.chainId, token.address),\n              );\n              const change = stat\n                ? parseFloat(stat.percent_change_24h)\n                : null;\n              const isPositive = change !== null && change >= 0;\n              const chainBadgeUrl = getChainBadgeUrl(env, token.chainId);\n              const shouldShowApr = isCrossdTokenAddress(token.address);\n\n              return (\n                <button\n                  type=\"button\"\n                  key={`${token.address}-${token.chainId}`}\n                  className=\"wi-token-item wi-token-item--button\"\n                  onClick={() => handleSelectTokenDetail(token)}\n                  aria-label={`View ${token.symbol} detail`}\n                  data-track=\"token_detail\"\n                  data-track-id={token.symbol}\n                >\n                  <div className=\"wi-token-left\">\n                    <div className=\"wi-token-icon-wrap\">\n                      <img\n                        src={token.icon_url}\n                        alt={token.symbol}\n                        className=\"wi-token-icon\"\n                        width={32}\n                        height={32}\n                        loading=\"lazy\"\n                        draggable={false}\n                        onLoad={handleImageLoad}\n                        onError={(e) => {\n                          e.currentTarget.src =\n                            \"https://contents.crosstoken.io/wallet/token/images/FT.png\";\n                        }}\n                      />\n                      {chainBadgeUrl && (\n                        <img\n                          src={chainBadgeUrl}\n                          alt=\"\"\n                          aria-hidden\n                          className=\"wi-token-chain-badge\"\n                          width={14}\n                          height={14}\n                          loading=\"lazy\"\n                          draggable={false}\n                          onError={(e) => {\n                            e.currentTarget.style.display = \"none\";\n                          }}\n                        />\n                      )}\n                    </div>\n                    <div className=\"wi-token-info\">\n                      <span className=\"wi-token-name-row\">\n                        <span className=\"wi-token-name\">{token.symbol}</span>\n                        {shouldShowApr && crossdAprPercent !== null && (\n                          <span className=\"wi-token-apr\">\n                            {formatCrossdAprPercent(crossdAprPercent)}\n                          </span>\n                        )}\n                      </span>\n                      {stat && (\n                        <div className=\"wi-token-price-row\">\n                          <span className=\"wi-token-price\">\n                            {formatPrice(stat.price)}\n                          </span>\n                          <span\n                            className=\"wi-token-change\"\n                            data-positive={isPositive}\n                            data-zero={change === 0 || undefined}\n                          >\n                            {change !== null &&\n                              (change === 0\n                                ? \"0%\"\n                                : `${isPositive ? \"+\" : \"-\"}${(isPositive ? Math.floor : Math.ceil)(Math.abs(change) * 100) / 100}%`)}\n                          </span>\n                        </div>\n                      )}\n                    </div>\n                  </div>\n                  <div className=\"wi-token-right\">\n                    <span className=\"wi-token-balance\">\n                      {formatWei(\n                        token.quantity.numeric,\n                        token.quantity.decimals,\n                      )}\n                    </span>\n                    {stat && (\n                      <span className=\"wi-token-value\">\n                        {formatTokenValue(\n                          token.quantity.numeric,\n                          token.quantity.decimals,\n                          stat.price,\n                        )}\n                      </span>\n                    )}\n                  </div>\n                </button>\n              );\n            })}\n\n            {showForgeToken &&\n              isForgeLoading &&\n              Array.from({ length: 3 }, (_, i) => (\n                <div key={`forge-sk-${i}`} className=\"wi-skeleton-item\">\n                  <div className=\"wi-skeleton-left\">\n                    <div className=\"wi-skeleton-block wi-skeleton-icon\" />\n                    <div className=\"wi-skeleton-lines\">\n                      <div\n                        className=\"wi-skeleton-block\"\n                        style={{ width: 64, height: 14 }}\n                      />\n                      <div\n                        className=\"wi-skeleton-block\"\n                        style={{ width: 80, height: 12 }}\n                      />\n                    </div>\n                  </div>\n                  <div className=\"wi-skeleton-right\">\n                    <div\n                      className=\"wi-skeleton-block\"\n                      style={{ width: 72, height: 14 }}\n                    />\n                    <div\n                      className=\"wi-skeleton-block\"\n                      style={{ width: 48, height: 12 }}\n                    />\n                  </div>\n                </div>\n              ))}\n\n            {showForgeToken &&\n              !isForgeLoading &&\n              !isLoading &&\n              tokens.length === 0 &&\n              sortedForgeHoldings.length === 0 && (\n                <div className=\"wi-empty\">No Tokens Found</div>\n              )}\n\n            {showForgeToken &&\n              sortedForgeHoldings.map((holding) => {\n                const imgUrl = forgeImageMap.get(holding.token) || \"\";\n                const chainBadgeUrl = getChainBadgeUrl(\n                  env,\n                  CHAIN_IDS[env].cross,\n                );\n                return (\n                  <div key={`forge-${holding.token}`} className=\"wi-token-item\">\n                    <div className=\"wi-token-left\">\n                      <div className=\"wi-token-icon-wrap\">\n                        <img\n                          src={\n                            imgUrl ||\n                            \"https://contents.crosstoken.io/wallet/token/images/FT.png\"\n                          }\n                          alt={holding.token_name}\n                          className=\"wi-token-icon\"\n                          width={32}\n                          height={32}\n                          loading=\"lazy\"\n                          draggable={false}\n                          onLoad={handleImageLoad}\n                          onError={(e) => {\n                            e.currentTarget.src =\n                              \"https://contents.crosstoken.io/wallet/token/images/FT.png\";\n                          }}\n                        />\n                        {chainBadgeUrl && (\n                          <img\n                            src={chainBadgeUrl}\n                            alt=\"\"\n                            aria-hidden\n                            className=\"wi-token-chain-badge\"\n                            width={14}\n                            height={14}\n                            loading=\"lazy\"\n                            draggable={false}\n                            onError={(e) => {\n                              e.currentTarget.style.display = \"none\";\n                            }}\n                          />\n                        )}\n                      </div>\n                      <div className=\"wi-token-info\">\n                        <span className=\"wi-token-name\">\n                          {holding.token_name}\n                        </span>\n                        <div className=\"wi-token-price-row\">\n                          <span className=\"wi-token-price\">\n                            {formatWei(holding.current_price, 18)} CROSS\n                          </span>\n                        </div>\n                      </div>\n                    </div>\n                    <div className=\"wi-token-right\">\n                      <span className=\"wi-token-balance\">\n                        {formatWei(holding.balance, 18)}\n                      </span>\n                      <span className=\"wi-token-value\">\n                        {formatPrice(holding.current_value_usd)}\n                      </span>\n                    </div>\n                  </div>\n                );\n              })}\n          </div>\n        </div>\n      )}\n\n      {navPosition === \"bottom\" && navContent && (\n        <div className=\"wi-nav-fixed\">{navContent}</div>\n      )}\n\n      {defaultDisconnectButton && (\n        <div className=\"wi-wallet-disconnect-row\">{defaultDisconnectButton}</div>\n      )}\n    </div>\n  );\n\n  const portfolioView = showPortfolio ? (\n    <div\n      key=\"portfolio\"\n      className={cn(\n        \"wi-view-inner wi-portfolio-body\",\n        hasTransitioned.current && \"wi-view-portfolio\",\n      )}\n    >\n      <WalletPortfolioBody\n        env={env}\n        theme={theme}\n        walletAddress={walletAddress}\n        showHeader={false}\n        onOutlink={onOutlink}\n        lpBalanceReader={lpBalanceReader}\n        stakingRewardsReader={stakingRewardsReader}\n        sendTransaction={sendTransaction}\n        estimateGas={estimateGas}\n        sections={portfolioSections}\n      />\n    </div>\n  ) : null;\n\n  const sendView = selectedSendToken ? (\n    <div\n      key=\"send\"\n      className={cn(\n        \"wi-view-inner wi-send-body\",\n        hasTransitioned.current && \"wi-view-send\",\n      )}\n    >\n      <SendFlow\n        env={env}\n        theme={theme}\n        walletAddress={walletAddress}\n        accountName={displayAccountName}\n        accounts={sendAccounts}\n        initialStep={sendInitialStep}\n        token={selectedSendToken}\n        tokens={tokens}\n        onTokenChange={setSelectedSendToken}\n        sendTransaction={sendTransaction}\n        getTransactionReceipt={getTransactionReceipt}\n        estimateGas={estimateGas}\n        getTokenPriceUsd={(t) => {\n          const s = statsMap.get(buildStatsKey(t.chainId, t.address));\n          if (!s?.price) return undefined;\n          const n = parseFloat(s.price);\n          return Number.isFinite(n) ? n : undefined;\n        }}\n        onClose={close}\n        onBackToWallet={handleBackToWallet}\n        onSubmitted={handleSendSubmitted}\n        onSuccess={handleConfirmSendSuccess}\n        onFailed={handleSendFailed}\n        onOutlink={onOutlink}\n      />\n    </div>\n  ) : null;\n\n  const resolvedBridgeTokens = React.useMemo<BridgeToken[]>(() => {\n    if (bridgeTokens) return bridgeTokens;\n\n    return tokens.map((token) => {\n      const stat = statsMap.get(buildStatsKey(token.chainId, token.address));\n      const price = stat?.price ? parseFloat(stat.price) : undefined;\n\n      return {\n        name: token.name,\n        symbol: token.symbol,\n        chainId: token.chainId,\n        address: token.address,\n        decimals: token.quantity.decimals,\n        balance: formatRawTokenAmount(\n          token.quantity.numeric,\n          token.quantity.decimals,\n        ),\n        iconUrl: token.icon_url,\n        priceUsd: Number.isFinite(price) ? price : undefined,\n      };\n    });\n  }, [bridgeTokens, statsMap, tokens]);\n\n  const bridgeView = (\n    <div\n      key=\"bridge\"\n      className={cn(\n        \"wi-view-inner wi-bridge-body\",\n        hasTransitioned.current && \"wi-view-send\",\n      )}\n    >\n      <BridgeFlow\n        env={env}\n        walletAddress={walletAddress}\n        tokens={resolvedBridgeTokens}\n        history={bridgeHistory}\n        termsUrl={termsUrl}\n        getQuote={getBridgeQuote}\n        getToTokens={getBridgeToTokens}\n        getApproval={getBridgeApproval}\n        approveBridge={approveBridge}\n        submitBridge={submitBridge}\n        onClose={close}\n        onBackToWallet={handleBackToWallet}\n      />\n    </div>\n  );\n\n  const tokenDetailView = selectedDetailToken ? (\n    <div\n      key=\"token-detail\"\n      className={cn(\n        \"wi-view-inner wi-token-detail-body\",\n        hasTransitioned.current && \"wi-view-send\",\n      )}\n    >\n      <TokenDetailView\n        env={env}\n        isMobile={isMobile}\n        token={selectedDetailToken}\n        walletAddress={walletAddress}\n        stat={statsMap.get(\n          buildStatsKey(\n            selectedDetailToken.chainId,\n            selectedDetailToken.address,\n          ),\n        )}\n        tokenInfo={infoMap.get(\n          buildCategoryKey(\n            selectedDetailToken.chainId,\n            selectedDetailToken.address,\n          ),\n        )}\n        showBalance={showBalance}\n        onBack={handleBackFromDetail}\n        onClose={close}\n        onSend={handleSendFromDetail}\n        onOutlink={onOutlink}\n      />\n    </div>\n  ) : null;\n\n  // ONEpop Available 카드는 지갑이 이미 불러온 ONEUSD 잔액을 그대로 쓴다 —\n  // 별도 API/props 없이 동일 소스라 값이 어긋나지 않는다. CROSS 체인의 CROSSD\n  // 컨트랙트(=ONEUSD)로 판별하고, 없으면 심볼로 폴백한다.\n  const onePopToken = React.useMemo(() => {\n    const crossChainId = CHAIN_IDS[env].cross;\n    return (\n      tokens.find(\n        (t) => t.chainId === crossChainId && isCrossdTokenAddress(t.address),\n      ) ??\n      tokens.find(\n        (t) =>\n          t.chainId === crossChainId &&\n          t.symbol.toUpperCase().includes(\"ONEUSD\"),\n      )\n    );\n  }, [tokens, env]);\n\n  const onePopBalanceDisplay = React.useMemo(() => {\n    if (!onePopToken) return undefined;\n    return formatWei(\n      onePopToken.quantity.numeric,\n      onePopToken.quantity.decimals,\n    );\n  }, [onePopToken]);\n\n  const onePopView = showOnePop ? (\n    <div\n      key=\"onepop\"\n      className={cn(\n        \"wi-view-inner wi-onepop-body\",\n        hasTransitioned.current && \"wi-view-send\",\n      )}\n    >\n      <OnePopBody\n        env={env}\n        balanceDisplay={onePopBalanceDisplay}\n        balanceIconUrl={onePopToken?.icon_url}\n        isBalanceLoading={isLoading && !onePopToken}\n        summary={onePopSummary}\n        logoSrc={onePopLogoSrc}\n        heroSrc={onePopHeroSrc}\n        getTokenSlot={onePopGetTokenSlot}\n        onBack={handleBackToWallet}\n        onClose={close}\n        onSend={onOnePopSend}\n        onClaim={onOnePopClaim}\n        onActivity={onOnePopActivity}\n        onRepliesSeen={onOnePopRepliesSeen}\n      />\n    </div>\n  ) : null;\n\n  // \"My Wallet\" 라벨은 멀티 계정 개념이 없는 CROSS 1.0 SDK 세션에서 쓰는\n  // 표식이라 chevron 을 눌러도 selectWallet 이 의미 있는 동작을 하지 않는다.\n  // 호출측이 onSelectWallet 을 그래도 넘기는 경우를 대비해 라벨 기준으로\n  // 클릭/꺾쇠를 차단한다.\n  const isSwitchableAccount =\n    !!onSelectWallet && accountName?.trim().toLowerCase() !== \"my wallet\";\n\n  const receiveAccountContent = (\n    <>\n      <span className=\"wi-receive-avatar\">\n        <span className=\"wi-avatar-fallback\" style={avatarStyle} />\n        {resolvedProfileUrl && (\n          <img\n            src={resolvedProfileUrl}\n            alt=\"\"\n            className=\"wi-avatar-img\"\n            width={24}\n            height={24}\n            draggable={false}\n            onLoad={handleImageLoad}\n            onError={(e) => {\n              e.currentTarget.style.display = \"none\";\n            }}\n          />\n        )}\n      </span>\n      <span className=\"wi-receive-account-name\">\n        {displayAccountName}\n      </span>\n      {isSwitchableAccount && (\n        <span className=\"wi-receive-chevron\" data-open={accountModalOpen} aria-hidden=\"true\">\n          <ChevronDownIcon size={16} />\n        </span>\n      )}\n    </>\n  );\n\n  const qrView = (\n    <div\n      key=\"qr\"\n      className={cn(\n        \"wi-view-inner wi-qr-body\",\n        hasTransitioned.current && \"wi-view-qr\",\n      )}\n    >\n      <div className=\"wi-receive-section\">\n        {isSwitchableAccount ? (\n          <button\n            type=\"button\"\n            className=\"wi-receive-account wi-receive-account--btn\"\n            onClick={() => { setAccountModalOpen(true); onSelectWallet?.(); }}\n            aria-label=\"Switch account\"\n          >\n            {receiveAccountContent}\n          </button>\n        ) : (\n          <div className=\"wi-receive-account\">{receiveAccountContent}</div>\n        )}\n\n        <div className=\"wi-qr-block\">\n          <div className=\"wi-qr-box\">\n            <QRCodeSVG\n              value={walletAddress}\n              size={180}\n              level=\"H\"\n              marginSize={2}\n              {...(qrLogoSrc && {\n                imageSettings: {\n                  src: qrLogoSrc,\n                  height: 33,\n                  width: 33,\n                  excavate: true,\n                },\n              })}\n            />\n            {!qrLogoSrc && (\n              <div className=\"wi-qr-logo\">\n                <QRLogoIcon size={33} />\n              </div>\n            )}\n          </div>\n          <p className=\"wi-qr-address\">{walletAddress}</p>\n        </div>\n\n        <button\n          type=\"button\"\n          className=\"wi-qr-copy-btn\"\n          onClick={handleCopyAddress}\n          data-track=\"address_copy\"\n        >\n          <span className=\"wi-qr-copy-icon\">\n            {copied ? <CheckIcon size={16} /> : <CopyIcon size={16} />}\n          </span>\n          <span className=\"wi-qr-copy-text\">\n            {copied ? \"Copied!\" : \"Copy address\"}\n          </span>\n        </button>\n      </div>\n\n      {legalLinks && <div className=\"wi-receive-footer\">{legalLinks}</div>}\n    </div>\n  );\n\n  const isSubView =\n    view === \"qr\" || view === \"portfolio\" || view === \"send\" || view === \"bridge\";\n\n  // 사용자 커스텀 Footer 슬롯은 기존 위치를 유지하고,\n  // 기본 Disconnect/Legal 링크는 Figma 기준 위치에 직접 배치한다.\n  const effectiveFooter =\n    footerContent !== null ? (\n      <div className=\"wi-builtin-footer\">\n        {footerContent}\n      </div>\n    ) : null;\n\n  const accountTextBlock = (\n    <div className=\"wi-account-text\">\n      {isSwitchableAccount ? (\n        <button\n          type=\"button\"\n          className=\"wi-account-row wi-account-row--btn\"\n          onClick={() => { setAccountModalOpen(true); onSelectWallet?.(); }}\n          aria-label=\"Switch account\"\n          aria-expanded={accountModalOpen}\n        >\n          <span className=\"wi-account-name\">{displayAccountName}</span>\n          <span className=\"wi-account-switch\" data-open={accountModalOpen} aria-hidden=\"true\">\n            <ChevronDownIcon size={20} />\n          </span>\n        </button>\n      ) : (\n        <div className=\"wi-account-row\">\n          <span className=\"wi-account-name\">{displayAccountName}</span>\n        </div>\n      )}\n      <button\n        type=\"button\"\n        className=\"wi-address-row-btn\"\n        onClick={handleCopyAddress}\n        aria-label=\"Copy address\"\n        data-track=\"address_copy\"\n      >\n        <span className=\"wi-account-address\">\n          {shortenAddress(walletAddress)}\n        </span>\n        <span className=\"wi-account-copy\" data-copied={copied}>\n          {copied ? <CheckIcon size={12} /> : <CopyIcon size={12} />}\n        </span>\n      </button>\n    </div>\n  );\n\n  const accountHeaderBlock = (\n    <div className=\"wi-account-block\">\n      <div className=\"wi-avatar\">\n        <div className=\"wi-avatar-fallback\" style={avatarStyle} />\n        {resolvedProfileUrl && (\n          <img\n            src={resolvedProfileUrl}\n            alt=\"\"\n            className=\"wi-avatar-img\"\n            width={28}\n            height={28}\n            draggable={false}\n            onLoad={handleImageLoad}\n            onError={(e) => {\n              e.currentTarget.style.display = \"none\";\n            }}\n          />\n        )}\n      </div>\n      {accountTextBlock}\n    </div>\n  );\n\n  const walletHeader = (\n    <div className=\"wi-sticky-header\">\n      <div className=\"wi-header wi-header--wallet\">\n        {accountHeaderBlock}\n        <div className=\"wi-header-actions\">\n          {showQR && showBridge && (\n            <button\n              type=\"button\"\n              className=\"wi-header-action wi-header-action--qr\"\n              onClick={effectiveReceive}\n              aria-label=\"Show QR code\"\n              data-track=\"receive\"\n            >\n              <QRIcon size={20} />\n            </button>\n          )}\n          <button\n            type=\"button\"\n            className=\"wi-header-action\"\n            onClick={close}\n            aria-label=\"Close\"\n          >\n            <CloseIcon size={24} />\n          </button>\n        </div>\n      </div>\n      <hr className=\"wi-divider\" />\n    </div>\n  );\n\n  const accountSubViewHeader = (\n    <div className=\"wi-sticky-header\">\n      <div className=\"wi-header wi-header--wallet wi-header--sub-account\">\n        <button\n          type=\"button\"\n          className=\"wi-header-back\"\n          onClick={handleBackToWallet}\n          aria-label=\"Back\"\n        >\n          <BackIcon size={24} />\n        </button>\n        <div className=\"wi-account-block wi-account-block--sub-account\">\n          {accountTextBlock}\n        </div>\n        <button\n          type=\"button\"\n          className=\"wi-header-action\"\n          onClick={close}\n          aria-label=\"Close\"\n        >\n          <CloseIcon size={24} />\n        </button>\n      </div>\n      <hr className=\"wi-divider\" />\n    </div>\n  );\n\n  const receiveHeader = (\n    <div className=\"wi-sticky-header\">\n      <div className=\"wi-header wi-header--receive\">\n        <button\n          type=\"button\"\n          className=\"wi-header-back\"\n          onClick={handleBackToWallet}\n          aria-label=\"Back\"\n        >\n          <BackIcon size={24} />\n        </button>\n        <span className=\"wi-receive-title\">Receive</span>\n        <button\n          type=\"button\"\n          className=\"wi-header-action\"\n          onClick={close}\n          aria-label=\"Close\"\n        >\n          <CloseIcon size={24} />\n        </button>\n      </div>\n    </div>\n  );\n\n  const contentBody = (\n    <>\n      {children}\n      {view === \"qr\"\n        ? receiveHeader\n        : view === \"send\" ||\n            view === \"bridge\" ||\n            view === \"token-detail\" ||\n            view === \"onepop\"\n          ? null\n          : isSubView\n            ? accountSubViewHeader\n            : walletHeader}\n\n      <div className=\"wi-view-container\">\n        {view === \"wallet\" && walletView}\n        {view === \"qr\" && qrView}\n        {view === \"portfolio\" && portfolioView}\n        {view === \"send\" && sendView}\n        {view === \"bridge\" && bridgeView}\n        {view === \"token-detail\" && tokenDetailView}\n        {view === \"onepop\" && onePopView}\n      </div>\n\n      {/* ONEpop 의 \"Get ONEUSD\"도 Buy 게이트 토스트를 띄우므로 해당 뷰에서도 노출. */}\n      {(view === \"wallet\" || view === \"onepop\") && actionToast && (\n        <div className=\"wi-action-toast\" role=\"status\" aria-live=\"polite\">\n          {actionToast}\n        </div>\n      )}\n\n      {view === \"wallet\" && sendToast && (\n        <a\n          className=\"wi-send-toast\"\n          href={sendToastUrl ?? \"#\"}\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          onClick={(e) => {\n            if (!sendToastUrl) {\n              e.preventDefault();\n              return;\n            }\n            handleOutlinkClick(\n              e,\n              sendToastUrl,\n              {\n                category: \"send\",\n                origin: \"send-transaction\",\n                payload: {\n                  chainId: sendToast.chainId,\n                  txHash: sendToast.txHash,\n                },\n              },\n              onOutlink,\n            );\n          }}\n        >\n          <span\n            className=\"wi-send-toast-icon\"\n            data-status={sendToast.status}\n            aria-hidden\n          >\n            {sendToast.status === \"pending\" ? (\n              <span className=\"wi-send-toast-spinner\" />\n            ) : sendToast.status === \"error\" ? (\n              <span className=\"wi-send-toast-error-dot\" />\n            ) : (\n              <ToastCheckIcon size={20} />\n            )}\n          </span>\n          <span className=\"wi-send-toast-copy\">\n            <span className=\"wi-send-toast-title\">\n              {sendToast.status === \"pending\"\n                ? \"Sending...\"\n                : sendToast.status === \"error\"\n                  ? \"Send failed\"\n                  : \"Send complete\"}\n            </span>\n            <span className=\"wi-send-toast-desc\">\n              {sendToast.status === \"pending\"\n                ? sendToast.description\n                : sendToast.status === \"error\"\n                  ? sendToast.description\n                : \"Tap to see your transaction\"}\n            </span>\n          </span>\n        </a>\n      )}\n\n      {effectiveFooter &&\n        view !== \"portfolio\" &&\n        view !== \"send\" &&\n        view !== \"token-detail\" &&\n        // ONEpop 뷰는 자체 \"Powered by ONEpop\" 푸터를 갖는다.\n        view !== \"onepop\" && (\n        <div className=\"wi-sticky-footer\">\n          <div className=\"wi-footer\" ref={footerRef}>\n            {effectiveFooter}\n          </div>\n        </div>\n      )}\n    </>\n  );\n\n  const contentStyle = {\n    ...portalStyle,\n    ...(footerHeight > 0\n      ? ({ \"--wi-footer-height\": `${footerHeight}px` } as React.CSSProperties)\n      : null),\n  };\n\n  // ONEpop 의 Get ONEUSD 위젯(`onePopGetTokenSlot`)은 자기 모달을 body 로\n  // 포털한다. 지갑 입장에서 그건 \"바깥\"이라, 위젯 모달을 클릭하거나 위젯이\n  // 포커스를 가져가는 순간 지갑이 닫히고 — ONEpop 뷰와 함께 위젯까지\n  // 언마운트돼 방금 연 모달이 사라진다. 위젯 셸(`[data-rd-shell]`) 안에서 온\n  // 상호작용이면 닫지 않는다. `onInteractOutside` 는 포인터/포커스 두 경로를\n  // 모두 받는다(vaul 은 자체 `onFocusOutside` 로 덮어써서 이쪽만 통한다).\n  const keepOpenForWidgetShell = React.useCallback(\n    (event: { detail: { originalEvent: Event }; preventDefault: () => void }) => {\n      const target = event.detail.originalEvent.target;\n      if (\n        target instanceof Element &&\n        target.closest(\"[data-rd-shell]\") !== null\n      ) {\n        event.preventDefault();\n      }\n    },\n    [],\n  );\n\n  if (isMobile) {\n    return (\n      <TrackingBoundary feature=\"wallet_info\">\n        <Drawer.Portal>\n          {modal && <Drawer.Overlay className=\"wi-drawer-overlay\" />}\n          <Drawer.Content\n            className={cn(\"wi-drawer-content\", className)}\n            data-theme={theme}\n            data-vaul-no-drag={\n              drawerDirection === \"left\" || drawerDirection === \"right\"\n                ? true\n                : undefined\n            }\n            style={contentStyle}\n            data-direction={drawerDirection}\n            data-no-balance={!showBalance || undefined}\n            data-has-footer={footerHeight > 0 || undefined}\n            data-view={view}\n            onInteractOutside={keepOpenForWidgetShell}\n          >\n            <Drawer.Title className=\"wi-sr-only\">My Wallet</Drawer.Title>\n            {contentBody}\n          </Drawer.Content>\n        </Drawer.Portal>\n      </TrackingBoundary>\n    );\n  }\n\n  return (\n    <TrackingBoundary feature=\"wallet_info\">\n      {modal && open && <div className=\"wi-drawer-overlay\" onClick={close} />}\n      <Popover.Portal>\n        <Popover.Content\n          align={align}\n          sideOffset={sideOffset}\n          className={cn(\"wi-popover-content\", className)}\n          data-theme={theme}\n          style={contentStyle}\n          data-no-balance={!showBalance || undefined}\n          data-has-footer={footerHeight > 0 || undefined}\n          data-view={view}\n          onInteractOutside={keepOpenForWidgetShell}\n        >\n          {contentBody}\n        </Popover.Content>\n      </Popover.Portal>\n    </TrackingBoundary>\n  );\n}\n","import BigNumber from \"bignumber.js\";\n\n/**\n * 숫자로 읽히는 형태만 통과시키는 정규식. `new BigNumber(\"Free\")` 는 NaN 을\n * 돌려주지 않고 **던진다** — 견적 행 값은 호출부(dApp)가 만드는 표시용 문자열이라\n * `\"Free\"`, `\"-\"`, `\"—\"` 같은 것이 섞여 오고, 그대로 넘기면 화면이 죽는다.\n */\nconst NUMERIC = /^[+-]?(\\d+\\.?\\d*|\\.\\d+)(e[+-]?\\d+)?$/i;\n\n/**\n * 표시용 문자열에서 숫자를 뽑는다. 숫자가 아니면 `null` — 던지지 않는다.\n * 콤마·퍼센트·공백은 제거한다(`\"1,234.5%\"` → 1234.5).\n */\nexport function parseNumeric(value?: unknown): BigNumber | null {\n  if (value === null || value === undefined) return null;\n  if (value instanceof BigNumber) return value.isFinite() ? value : null;\n  if (typeof value === \"number\") {\n    return Number.isFinite(value) ? new BigNumber(value) : null;\n  }\n  const cleaned = String(value).replace(/[,%\\s]/g, \"\");\n  if (!NUMERIC.test(cleaned)) return null;\n  const parsed = new BigNumber(cleaned);\n  return parsed.isFinite() ? parsed : null;\n}\n","import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { TokenBalance, TokenBalanceResponse } from \"../types\";\nimport { CHAIN_IDS } from \"../constants\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const USER_BALANCE_QUERY_KEY = \"user-balance\";\n\nasync function fetchBalance(\n  baseUrl: string,\n  chainId: number,\n  walletAddress: string,\n  signal?: AbortSignal,\n): Promise<TokenBalance[]> {\n  const params = new URLSearchParams({\n    networkId: `eip155:${chainId}`,\n    account: walletAddress,\n  });\n  const res = await fetch(`${baseUrl}/v1/public/token/balance?${params}`, {\n    cache: \"no-store\",\n    signal,\n  });\n  if (!res.ok) {\n    throw new Error(`Failed to fetch token balance: ${res.status}`);\n  }\n  const json: TokenBalanceResponse = await res.json();\n  if (json.code !== 200) {\n    throw new Error(`Token balance API error: ${json.code}`);\n  }\n  return json.data;\n}\n\nexport function useTokenBalance(\n  env: Environment,\n  walletAddress: string,\n  active = true,\n) {\n  const chains = CHAIN_IDS[env];\n  const chainIds = [chains.cross, chains.bsc];\n\n  const results = useQueries({\n    queries: chainIds.map((chainId) => ({\n      queryKey: [USER_BALANCE_QUERY_KEY, env, walletAddress, chainId],\n      queryFn: async ({ signal }: { signal?: AbortSignal }) =>\n        fetchBalance(\n          await getApiBase(\"walletServer\", env),\n          chainId,\n          walletAddress,\n          signal,\n        ),\n      enabled: !!walletAddress && active,\n      staleTime: 0,\n      refetchInterval: active ? 5000 : false,\n    })),\n  });\n\n  const isLoading = results.some((r) => r.isLoading);\n  const isError = results.some((r) => r.isError);\n\n  const tokens = useMemo(() => {\n    const merged: TokenBalance[] = [];\n    for (const r of results) {\n      if (r.data) merged.push(...r.data);\n    }\n    return merged;\n  }, [results]);\n\n  return { tokens, isLoading, isError };\n}\n","import { useQuery, useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type {\n  ForgeHolding,\n  ForgeHoldingsResponse,\n  ForgeTokenDetailResponse,\n} from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const FORGE_HOLDINGS_QUERY_KEY = \"forge-holdings\";\nexport const FORGE_TOKEN_IMAGE_QUERY_KEY = \"forge-token-image\";\n\nasync function fetchForgeHoldings(\n  baseUrl: string,\n  walletAddress: string,\n  signal?: AbortSignal,\n): Promise<ForgeHolding[]> {\n  const res = await fetch(\n    `${baseUrl}/v1/users/${walletAddress}/holdings`,\n    { cache: \"no-store\", signal },\n  );\n  if (!res.ok) {\n    throw new Error(`Failed to fetch forge holdings: ${res.status}`);\n  }\n  const json: ForgeHoldingsResponse = await res.json();\n  if (!json.success) {\n    throw new Error(\"Forge holdings API error\");\n  }\n  return json.data.items;\n}\n\nasync function fetchForgeTokenImage(\n  baseUrl: string,\n  tokenAddress: string,\n  signal?: AbortSignal,\n): Promise<{ address: string; image_url: string }> {\n  const res = await fetch(`${baseUrl}/v1/tokens/${tokenAddress}`, {\n    cache: \"no-store\",\n    signal,\n  });\n  if (!res.ok) {\n    return { address: tokenAddress, image_url: \"\" };\n  }\n  const json: ForgeTokenDetailResponse = await res.json();\n  if (!json.success) {\n    return { address: tokenAddress, image_url: \"\" };\n  }\n  return {\n    address: tokenAddress,\n    image_url: json.data.token.image_url,\n  };\n}\n\nexport function useForgeHoldings(\n  env: Environment,\n  walletAddress: string,\n  active = true,\n) {\n  const { data, ...rest } = useQuery({\n    queryKey: [FORGE_HOLDINGS_QUERY_KEY, env, walletAddress],\n    queryFn: async ({ signal }) =>\n      fetchForgeHoldings(\n        await getApiBase(\"bondingCurve\", env),\n        walletAddress,\n        signal,\n      ),\n    enabled: !!walletAddress && active,\n    staleTime: 0,\n    refetchInterval: active ? 5000 : false,\n  });\n\n  return { holdings: data ?? [], ...rest };\n}\n\nexport function useForgeTokenImages(\n  env: Environment,\n  tokenAddresses: string[],\n  active = true,\n) {\n  const results = useQueries({\n    queries: tokenAddresses.map((addr) => ({\n      queryKey: [FORGE_TOKEN_IMAGE_QUERY_KEY, env, addr],\n      queryFn: async ({ signal }: { signal?: AbortSignal }) =>\n        fetchForgeTokenImage(await getApiBase(\"bondingCurve\", env), addr, signal),\n      enabled: active,\n      staleTime: Infinity,\n    })),\n  });\n\n  const imageMap = useMemo(() => {\n    const map = new Map<string, string>();\n    for (const r of results) {\n      if (r.data) {\n        map.set(r.data.address, r.data.image_url);\n      }\n    }\n    return map;\n  }, [results]);\n\n  return { imageMap };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { TokenStats, TokenStatsResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const TOKEN_STATS_QUERY_KEY = \"token-stats\";\n\nfunction buildStatsKey(chainId: number, address: string): string {\n  return `${chainId}:${address.toLowerCase()}`;\n}\n\nexport function useTokenStats(env: Environment, enabled = true) {\n  const { data, ...rest } = useQuery<TokenStatsResponse>({\n    queryKey: [TOKEN_STATS_QUERY_KEY, env],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"walletServer\", env);\n      const res = await fetch(`${baseUrl}/v1/public/token/stats`, {\n        cache: \"no-store\",\n        signal,\n      });\n      if (!res.ok) {\n        throw new Error(`Failed to fetch token stats: ${res.status}`);\n      }\n      const json: TokenStatsResponse = await res.json();\n      if (json.code !== 200) {\n        throw new Error(`Token stats API error: ${json.code}`);\n      }\n      return json;\n    },\n    enabled,\n    staleTime: 30_000,\n  });\n\n  const statsMap = useMemo(() => {\n    const map = new Map<string, TokenStats>();\n    if (!data?.data) return map;\n    for (const stat of data.data) {\n      const key = buildStatsKey(stat.chain_id, stat.address);\n      map.set(key, stat);\n    }\n    return map;\n  }, [data]);\n\n  return { statsMap, ...rest };\n}\n\nexport { buildStatsKey };\n","import { useQuery } from \"@tanstack/react-query\";\n\nexport const CROSSD_APR_QUERY_KEY = \"crossd-apr\";\nconst CROSSD_APR_STALE_TIME_MS = 60_000;\n\n/**\n * CROSSD 대시보드 overview API. prd 환경만 존재하므로 URL을 고정한다.\n * 응답의 `est_last_epoch_apy`는 비율값(1 == 100%, 0.05 == 5%).\n */\nexport const CROSSD_OVERVIEW_URL =\n  \"https://crossd-dashboard-api.crossdefi.io/api/v1/overview\";\n\ninterface CrossdOverviewResponse {\n  code: number;\n  message: string;\n  data: {\n    est_last_epoch_apy: string;\n  };\n}\n\nlet cachedApr: { value: number; expiresAt: number } | undefined;\nlet aprRequest: Promise<number> | undefined;\n\nexport function __clearCrossdAprCacheForTest(): void {\n  cachedApr = undefined;\n  aprRequest = undefined;\n}\n\n/** Shared by WalletInfo and GetOneUsd so colocated widgets issue one request. */\nexport function fetchCrossdAprPercent(): Promise<number> {\n  if (cachedApr && cachedApr.expiresAt > Date.now()) {\n    return Promise.resolve(cachedApr.value);\n  }\n  if (aprRequest) return aprRequest;\n\n  aprRequest = (async () => {\n    const res = await fetch(CROSSD_OVERVIEW_URL, { cache: \"no-store\" });\n    if (!res.ok) {\n      throw new Error(`Failed to fetch CROSSD overview: ${res.status}`);\n    }\n    const json: CrossdOverviewResponse = await res.json();\n    if (json.code !== 200) {\n      throw new Error(`CROSSD overview API error: ${json.code}`);\n    }\n    const ratio = parseFloat(json.data.est_last_epoch_apy);\n    if (!Number.isFinite(ratio)) {\n      throw new Error(\"CROSSD overview returned a non-numeric APR\");\n    }\n    const value = ratio * 100;\n    cachedApr = { value, expiresAt: Date.now() + CROSSD_APR_STALE_TIME_MS };\n    return value;\n  })().finally(() => {\n    aprRequest = undefined;\n  });\n\n  return aprRequest;\n}\n\nexport function formatCrossdAprPercent(aprPercent: number): string {\n  const magnitude = Math.abs(aprPercent);\n  const rounded =\n    (aprPercent >= 0 ? Math.floor : Math.ceil)(magnitude * 100) / 100;\n  return `APR ${aprPercent >= 0 ? \"+\" : \"-\"}${rounded}%`;\n}\n\n/**\n * CROSSD APR(`est_last_epoch_apy`)를 백분율 숫자로 반환한다.\n * API가 prd 전용이라 env와 무관하게 항상 prd URL에서 fetch해 표기한다.\n */\nexport function useCrossdApr() {\n  const { data, ...rest } = useQuery<number>({\n    queryKey: [CROSSD_APR_QUERY_KEY],\n    queryFn: fetchCrossdAprPercent,\n    staleTime: CROSSD_APR_STALE_TIME_MS,\n  });\n\n  return { aprPercent: data ?? null, ...rest };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { TokenInfo, TokenInfoResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const TOKEN_INFO_QUERY_KEY = \"token-info\";\n\nfunction buildCategoryKey(chainId: number, address: string): string {\n  return `${address.toLowerCase()}_${chainId}`;\n}\n\nexport function useTokenInfo(env: Environment, enabled = true) {\n  const { data, ...rest } = useQuery<TokenInfoResponse>({\n    queryKey: [TOKEN_INFO_QUERY_KEY, env],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"walletServer\", env);\n      const res = await fetch(`${baseUrl}/v1/public/token/info`, {\n        cache: \"no-store\",\n        signal,\n      });\n      if (!res.ok) {\n        throw new Error(`Failed to fetch token info: ${res.status}`);\n      }\n      const json: TokenInfoResponse = await res.json();\n      if (json.code !== 200) {\n        throw new Error(`Token info API error: ${json.code}`);\n      }\n      return json;\n    },\n    enabled,\n    staleTime: Infinity,\n  });\n\n  const categoryMap = useMemo(() => {\n    const map = new Map<string, string>();\n    if (!data?.data) return map;\n    for (const info of data.data) {\n      const key = buildCategoryKey(info.chain_id, info.address);\n      map.set(key, info.category);\n    }\n    return map;\n  }, [data]);\n\n  // 토큰 상세화면(소셜 링크·배포 여부 등)에서 단건 조회하기 위한 전체 메타 맵.\n  const infoMap = useMemo(() => {\n    const map = new Map<string, TokenInfo>();\n    if (!data?.data) return map;\n    for (const info of data.data) {\n      map.set(buildCategoryKey(info.chain_id, info.address), info);\n    }\n    return map;\n  }, [data]);\n\n  return { categoryMap, infoMap, ...rest };\n}\n\nexport { buildCategoryKey };\n","import { useMemo } from \"react\";\nimport BigNumber from \"bignumber.js\";\n\nimport { parseNumeric } from \"../../../lib/numeric\";\nimport type { Environment } from \"../../../types\";\nimport { useTokenBalance } from \"./use-token-balance\";\nimport { useTokenStats, buildStatsKey } from \"./use-token-stats\";\nimport { formatTokenValueForDisplay } from \"../../../lib/format\";\n\nexport interface TotalValueResult {\n  /** BigNumber 원시 문자열 (예: \"1234.56789\") */\n  totalUsd: string;\n  /** 포맷된 값 (예: \"1,234.56\") */\n  displayValue: string;\n  /** true면 UI에서 앞에 \"≤\" 표기 필요 */\n  isLowerBound: boolean;\n  isLoading: boolean;\n}\n\n/**\n * 지갑 보유 토큰(useTokenBalance)들의 USD 합계를 계산.\n * cross-wallet-web-monorepo의 useTotalValue와 동일 정책:\n *  total = Σ (amount × price)\n */\nexport function useTotalValue(\n  env: Environment,\n  walletAddress: string,\n  enabled: boolean,\n): TotalValueResult {\n  const { tokens, isLoading: isBalanceLoading } = useTokenBalance(\n    env,\n    walletAddress,\n    enabled,\n  );\n  const { statsMap } = useTokenStats(env, enabled);\n\n  return useMemo(() => {\n    let total = new BigNumber(0);\n    for (const t of tokens) {\n      const stat = statsMap.get(buildStatsKey(t.chainId, t.address));\n      if (!stat) continue;\n      // API 문자열을 그대로 넘기면 안 된다 — `new BigNumber(\"-\")` 는 NaN 이\n      // 아니라 예외를 던져서, 값 하나가 깨지면 지갑 화면 전체가 죽는다.\n      const quantity = parseNumeric(t.quantity.numeric);\n      const price = parseNumeric(stat.price);\n      if (!quantity || !price) continue;\n      const value = quantity.shiftedBy(-t.quantity.decimals).multipliedBy(price);\n      if (value.isNaN()) continue;\n      total = total.plus(value);\n    }\n\n    const totalUsd = total.toFixed();\n    const { displayValue, isLowerBound } = formatTokenValueForDisplay(totalUsd);\n\n    return {\n      totalUsd,\n      displayValue,\n      isLowerBound,\n      isLoading: isBalanceLoading,\n    };\n  }, [tokens, statsMap, isBalanceLoading]);\n}\n","import { WALLET_LABELS } from \"@nexus-cross/crossx-design-system\";\nimport { WALLET_REGISTRY, type WalletId } from \"./constants\";\n\n/**\n * 커넥트 모달에서 마지막으로 고른 항목을 기록한다.\n *\n * 용도는 하나 — `WalletInfo`가 계정 라벨을 표시할 때의 폴백. 호출측이\n * `accountName` / `connectorName` / `connectorId` 를 전부 빼먹으면 계정 라벨이\n * 익명 \"Account\"로 떨어지는데, 사용자가 모달에서 직접 고른 지갑이 있다면 그\n * 이름을 쓰는 게 가장 정확하다. 리로드 후(모달을 다시 거치지 않는 자동 재연결)\n * 에도 유지돼야 하므로 localStorage에 남긴다.\n *\n * 명시 prop 보다 항상 아래 순위이므로, 값이 낡아도 호출측이 이름을 넘기는\n * 순간 무시된다.\n */\nconst STORAGE_KEY = \"dappui.wcm.last-selected\";\n\n/** 소셜 로그인은 embedded 지갑으로 귀결되므로 브랜드명 하나로 묶는다. */\nconst SOCIAL_IDS = new Set([\"google\", \"apple\"]);\n\nexport function recordSelectedWallet(id: string): void {\n  try {\n    window.localStorage.setItem(STORAGE_KEY, id);\n  } catch {\n    // Private 모드 / 용량 초과 등. 폴백 품질 문제일 뿐이라 조용히 넘긴다.\n  }\n}\n\n/**\n * 마지막 선택 항목의 표시 이름. 모달에 표기됐던 라벨을 그대로 돌려준다.\n * 기록이 없거나 레지스트리에 없는 id면 `undefined`.\n *\n * 브라우저 전용 — SSR 중에는 항상 `undefined`이므로 호출측은 effect 안에서\n * 읽어 hydration mismatch를 피해야 한다.\n */\nexport function readSelectedWalletName(): string | undefined {\n  let id: string | null = null;\n  try {\n    id = window.localStorage.getItem(STORAGE_KEY);\n  } catch {\n    return undefined;\n  }\n  if (!id) return undefined;\n  if (SOCIAL_IDS.has(id)) return WALLET_LABELS.default;\n  return WALLET_REGISTRY[id as WalletId]?.name;\n}\n","import { WALLET_LABELS } from \"@nexus-cross/crossx-design-system\";\nimport {\n  AppleIcon,\n  BinanceIcon,\n  CROSSxIcon,\n  GoogleIcon,\n  MetaMaskIcon,\n  TronIcon,\n  Verse8Icon,\n} from \"./components/icons\";\nimport type { SocialConfig, WalletConfig } from \"./types\";\n\nexport const WALLET_REGISTRY = {\n  cross_embedded: {\n    id: \"cross_embedded\",\n    name: WALLET_LABELS.social,\n    description: \"Sign in with Google or Apple\",\n    icon: CROSSxIcon,\n  },\n  cross_wallet: {\n    id: \"cross_wallet\",\n    name: WALLET_LABELS.app,\n    description: \"App approval is required\",\n    icon: CROSSxIcon,\n    featured: true,\n  },\n  cross_extension: {\n    id: \"cross_extension\",\n    name: WALLET_LABELS.extension,\n    description: \"Browser extension approval is required\",\n    icon: CROSSxIcon,\n    rdns: \"nexus.to.crosswallet.desktop\",\n    installUrl:\n      \"https://chromewebstore.google.com/detail/crossx/nninbdadmocnokibpaaohnoepbnpdgcg\",\n    visibility: \"desktop-only\" as const,\n  },\n  metamask: {\n    id: \"metamask\",\n    name: \"MetaMask\",\n    description: \"Browser extension approval is required\",\n    icon: MetaMaskIcon,\n    rdns: \"io.metamask\",\n  },\n  binance: {\n    id: \"binance\",\n    name: \"Binance\",\n    description: \"Mobile app only — Add CROSS network first\",\n    icon: BinanceIcon,\n  },\n  verse8: {\n    id: \"verse8\",\n    name: \"Verse8 Wallet\",\n    description: \"Optimized for Forge and Verse8 ecosystem\",\n    icon: Verse8Icon,\n    badge: \"For Forge\",\n  },\n  tron: {\n    id: \"tron\",\n    name: \"Tron Wallet\",\n    description: \"Dedicated for Tron-based transactions and assets\",\n    icon: TronIcon,\n    badge: \"For SHOP\",\n  },\n} satisfies Record<string, WalletConfig>;\n\nexport type WalletId = keyof typeof WALLET_REGISTRY;\n\nexport function resolveWallet(id: WalletId): WalletConfig {\n  return WALLET_REGISTRY[id];\n}\n\nexport const SOCIAL_REGISTRY = {\n  google: {\n    id: \"google\",\n    name: \"Google\",\n    icon: GoogleIcon,\n  },\n  apple: {\n    id: \"apple\",\n    name: \"Apple\",\n    icon: AppleIcon,\n  },\n} satisfies Record<string, SocialConfig>;\n\nexport type SocialId = keyof typeof SOCIAL_REGISTRY;\n\nexport function resolveSocial(id: SocialId): SocialConfig {\n  return SOCIAL_REGISTRY[id];\n}\n","// ONEwallet brand mark (pink→orange gradient). Rendered as an isolated\n// <img> data-URI so the SVG's internal ids (mask/filter/gradients) can't\n// collide when the icon is shown multiple times in the same modal.\nconst ONE_WALLET_SVG = `<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 640 640\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_75048_28969)\"><rect width=\"640\" height=\"640\" rx=\"100\" fill=\"white\"/><path d=\"M640.092 -0.18042H0.0917969V639.82H640.092V-0.18042Z\" fill=\"#CE3E35\"/><path d=\"M43.6074 104.484C43.6074 71.3473 70.4703 44.4844 103.607 44.4844H536.393C569.53 44.4844 596.393 71.3473 596.393 104.484V346.514H43.6074V104.484Z\" fill=\"white\"/><g filter=\"url(#filter0_d_75048_28969)\"><path d=\"M639.908 639.822H0.0917969V158.676C0.0917969 136.585 18.0004 118.676 40.0918 118.676H218.544C237.426 118.676 252.868 132.846 264.251 147.912C277.196 165.045 297.355 176.058 319.999 176.058C342.644 176.058 362.803 165.045 375.748 147.912C387.131 132.846 402.573 118.676 421.455 118.676H599.908C622 118.676 639.908 136.585 639.908 158.676V639.822Z\" fill=\"url(#paint0_linear_75048_28969)\"/></g><path d=\"M223.621 639.503H639.909V181.499L223.621 448.166V639.503Z\" fill=\"url(#paint1_linear_75048_28969)\"/><path d=\"M223.645 639.819H0.0917969V212.869L223.645 449.197V639.819Z\" fill=\"url(#paint2_linear_75048_28969)\"/><g filter=\"url(#filter1_d_75048_28969)\"><path d=\"M317.103 250.341L228.024 301.829C212.311 310.864 206.867 331.162 215.899 346.881C224.93 362.6 245.221 368.046 260.933 359.01L336.28 315.444L300.401 353.936V504.934C300.401 523.129 315.247 537.981 333.434 537.981C351.621 537.981 366.468 523.129 366.468 504.934V278.932C366.468 272.62 364.489 266.431 362.014 262.347C352.735 246.752 332.692 241.182 316.979 250.217L317.103 250.341Z\" fill=\"white\"/></g></g><defs><filter id=\"filter0_d_75048_28969\" x=\"-19.9082\" y=\"93.6763\" width=\"679.816\" height=\"561.146\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\"><feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/><feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\" result=\"hardAlpha\"/><feOffset dy=\"-5\"/><feGaussianBlur stdDeviation=\"10\"/><feComposite in2=\"hardAlpha\" operator=\"out\"/><feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.892803 0 0 0 0 0.220099 0 0 0 0 0.293135 0 0 0 0.5 0\"/><feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow_75048_28969\"/><feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect1_dropShadow_75048_28969\" result=\"shape\"/></filter><filter id=\"filter1_d_75048_28969\" x=\"190.367\" y=\"224.71\" width=\"211.348\" height=\"348.518\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\"><feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/><feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\" result=\"hardAlpha\"/><feOffset dx=\"7.03953\" dy=\"7.03953\"/><feGaussianBlur stdDeviation=\"14.1039\"/><feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.831373 0 0 0 0 0.113725 0 0 0 0 0.356863 0 0 0 0.3 0\"/><feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow_75048_28969\"/><feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect1_dropShadow_75048_28969\" result=\"shape\"/></filter><linearGradient id=\"paint0_linear_75048_28969\" x1=\"-74.0158\" y1=\"54.6376\" x2=\"542.251\" y2=\"802.323\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#F3752D\"/><stop offset=\"1\" stop-color=\"#EC1272\"/></linearGradient><linearGradient id=\"paint1_linear_75048_28969\" x1=\"159.749\" y1=\"206.729\" x2=\"471.286\" y2=\"474.513\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"white\" stop-opacity=\"0.5\"/><stop offset=\"1\" stop-color=\"white\" stop-opacity=\"0\"/></linearGradient><linearGradient id=\"paint2_linear_75048_28969\" x1=\"399.134\" y1=\"82.7297\" x2=\"104.683\" y2=\"466.123\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"white\" stop-opacity=\"0.5\"/><stop offset=\"1\" stop-color=\"white\" stop-opacity=\"0\"/></linearGradient><clipPath id=\"clip0_75048_28969\"><rect width=\"640\" height=\"640\" rx=\"100\" fill=\"white\"/></clipPath></defs></svg>`;\n\nconst ONE_WALLET_ICON = `data:image/svg+xml,${encodeURIComponent(ONE_WALLET_SVG)}`;\n\nexport function CROSSxIcon() {\n  return (\n    <img\n      src={ONE_WALLET_ICON}\n      width={40}\n      height={40}\n      alt=\"\"\n      style={{ display: \"block\" }}\n    />\n  );\n}\n\nexport function MetaMaskIcon() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"40\"\n      height=\"40\"\n      viewBox=\"0 0 40 40\"\n      fill=\"none\"\n    >\n      <g clipPath=\"url(#clip0_1331_27065)\">\n        <path\n          d=\"M37.2488 38.4674L28.6626 35.8175L22.1876 39.8295L17.6699 39.8274L11.191 35.8175L2.60875 38.4674L-0.00195312 29.3328L2.60875 19.1947L-0.00195312 10.6234L2.60875 0L16.0192 8.30453H23.838L37.2488 0L39.8595 10.6234L37.2488 19.1947L39.8595 29.3328L37.2488 38.4674Z\"\n          fill=\"#FF5C16\"\n        />\n        <path\n          d=\"M2.60938 0L16.0201 8.31037L15.4868 14.0137L2.60938 0ZM11.1919 29.3366L17.0925 33.9956L11.1919 35.8175V29.3366ZM16.6209 21.6342L15.4868 14.0175L8.22768 19.1971L8.22374 19.195V19.1988L8.24628 24.5302L11.1899 21.6345L16.6209 21.6342ZM37.2477 0L23.837 8.31037L24.3685 14.0137L37.2477 0ZM28.6654 29.3366L22.7646 33.9956L28.6654 35.8175V29.3366ZM31.6313 19.1985V19.1947L31.6297 19.1968L24.3705 14.0175L23.2364 21.6342H28.6651L31.6108 24.5296L31.6313 19.1985Z\"\n          fill=\"#FF5C16\"\n        />\n        <path\n          d=\"M11.191 35.8175L2.60875 38.4674L-0.00195312 29.3366H11.191V35.8175ZM16.62 21.6321L18.2595 32.6435L15.9873 26.5208L8.24368 24.5296L11.189 21.6324H16.62V21.6321ZM28.6662 35.8175L37.2488 38.4674L39.8595 29.3366H28.6662V35.8175ZM23.2375 21.6321L21.598 32.6435L23.8699 26.5208L31.6138 24.5296L28.6665 21.6324H23.2375V21.6321Z\"\n          fill=\"#E34807\"\n        />\n        <path\n          d=\"M-0.00195312 29.3328L2.60875 19.1947H8.22284L8.2434 24.5278L15.9876 26.519L18.2595 32.6414L17.0916 33.9897L11.191 29.3307H-0.00195312V29.3328ZM39.8595 29.3328L37.2488 19.1947H31.6344L31.6138 24.5278L23.8702 26.519L21.598 32.6414L22.7657 33.9897L28.6665 29.3307H39.8595V29.3328ZM23.838 8.30444H16.0192L15.4882 14.0078L18.2597 32.6355H21.598L24.3713 14.0078L23.838 8.30444Z\"\n          fill=\"#FF8D5D\"\n        />\n        <path\n          d=\"M2.60875 0L-0.00195312 10.6234L2.60875 19.1947H8.22284L15.4859 14.0137L2.60875 0ZM14.9975 23.8438H12.4541L11.0693 25.2508L15.9893 26.515L14.9975 23.842V23.8438ZM37.2488 0L39.8595 10.6234L37.2488 19.1947H31.6344L24.3716 14.0137L37.2488 0ZM24.8637 23.8438H27.4107L28.7955 25.2526L23.8699 26.5191L24.8637 23.8418V23.8438ZM22.1857 36.1956L22.7659 33.9936L21.598 32.6453H18.2572L17.0896 33.9936L17.6696 36.1956\"\n          fill=\"#661800\"\n        />\n        <path\n          d=\"M22.1857 36.1951V39.831H17.6699V36.1951H22.1857Z\"\n          fill=\"#C0C4CD\"\n        />\n        <path\n          d=\"M11.1914 35.8131L17.672 39.8289V36.193L17.0917 33.9912L11.1914 35.8131ZM28.6649 35.8131L22.1841 39.8289V36.193L22.7644 33.9912L28.6649 35.8131Z\"\n          fill=\"#E7EBF6\"\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_1331_27065\">\n          <rect width=\"40\" height=\"40\" fill=\"white\" />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n\nexport function BinanceIcon() {\n  return (\n    <svg\n      width=\"40\"\n      height=\"40\"\n      viewBox=\"0 0 40 40\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M12.23 16.81L20 9.04L27.77 16.81L32.29 12.29L20 0L7.71 12.29L12.23 16.81Z\"\n        fill=\"#F3BA2F\"\n      />\n      <path\n        d=\"M9.04 20L4.52 15.48L0 20L4.52 24.52L9.04 20Z\"\n        fill=\"#F3BA2F\"\n      />\n      <path\n        d=\"M12.23 23.19L20 30.96L27.77 23.19L32.29 27.71L20 40L7.71 27.71L12.23 23.19Z\"\n        fill=\"#F3BA2F\"\n      />\n      <path\n        d=\"M35.48 24.52L40 20L35.48 15.48L30.96 20L35.48 24.52Z\"\n        fill=\"#F3BA2F\"\n      />\n      <path\n        d=\"M24.58 20L20 15.41L16.61 18.8L16.22 19.19L15.42 19.99L20 24.59L24.59 20L24.58 20Z\"\n        fill=\"#F3BA2F\"\n      />\n    </svg>\n  );\n}\n\nexport function Verse8Icon() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"247\"\n      height=\"246\"\n      viewBox=\"0 0 247 246\"\n      fill=\"none\"\n    >\n      <rect width=\"247.005\" height=\"245.105\" rx=\"36\" fill=\"#121212\" />\n      <path\n        d=\"M89.1233 136.695V59.7551H118.481V140.524L74.5862 184.774L30.4787 140.524V107.55H59.9073V136.695L74.5862 151.303L89.1233 136.695Z\"\n        fill=\"white\"\n      />\n      <path\n        d=\"M59.8364 99.8915V59.7551H12.0414L30.4787 77.8377V99.8915H59.8364Z\"\n        fill=\"#2FD7FF\"\n      />\n      <path\n        d=\"M199.534 59.9334C202.562 60.0484 208.427 60.4824 212.031 63.6902C215.635 66.898 216.937 71.0498 216.937 73.1199V104.017L198.767 114.828L216.937 125.561V172.713C216.937 174.511 216.405 178.276 211.034 181.377C205.664 184.477 202.613 184.969 201.758 184.827H143.949C141.879 184.904 136.712 184.182 132.602 180.686C128.493 177.19 128.462 175.216 128.462 173.515V125.561L146.173 114.828L128.462 104.017V71.1268C128.462 69.721 130.109 65.7781 133.237 63.5701C137.147 60.8101 141.112 59.9334 144.409 59.9334H199.534ZM158.3 122.264V166.221H187.033V122.264H158.3ZM158.3 107.794H187.033V78.5272H158.3V107.794Z\"\n        fill=\"white\"\n      />\n    </svg>\n  );\n}\n\nexport function TronIcon() {\n  return (\n    <svg\n      width=\"40\"\n      height=\"40\"\n      viewBox=\"0 0 28 28\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <g clipPath=\"url(#clip0_1185_7785)\">\n        <path\n          d=\"M14 28C21.75 28 28 21.75 28 14C28 6.25 21.75 0 14 0C6.25 0 0 6.25 0 14C0 21.75 6.25 28 14 28Z\"\n          fill=\"#EB0029\"\n        />\n        <path\n          d=\"M19.4975 8.36232L6 6L13.103 23L23 11.5311L19.4975 8.36232ZM19.2801 9.4034L21.3449 11.27L15.6975 12.2424L19.2801 9.4034ZM14.4714 12.0479L8.51876 7.35252L18.247 9.05524L14.4714 12.0479ZM14.0467 12.8782L13.0765 20.5098L7.84385 7.98492L14.0467 12.8782ZM14.9454 13.2835L21.1983 12.2064L14.026 20.518L14.9454 13.2835Z\"\n          fill=\"white\"\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_1185_7785\">\n          <rect width=\"28\" height=\"28\" fill=\"white\" />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n\nexport function GoogleIcon() {\n  return (\n    <svg\n      width=\"40\"\n      height=\"40\"\n      viewBox=\"0 0 48 48\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        fill=\"#FFC107\"\n        d=\"M43.6 20.5H42V20H24v8h11.3A12 12 0 1 1 24 12c3 0 5.8 1.2 7.9 3l5.7-5.7A20 20 0 1 0 44 24c0-1.2-.1-2.4-.4-3.5z\"\n      />\n      <path\n        fill=\"#FF3D00\"\n        d=\"M6.3 14.7l6.6 4.8A12 12 0 0 1 24 12c3 0 5.8 1.2 7.9 3l5.7-5.7A20 20 0 0 0 6.3 14.7z\"\n      />\n      <path\n        fill=\"#4CAF50\"\n        d=\"M24 44c5.2 0 10-2 13.6-5.2l-6.3-5.3A12 12 0 0 1 12.7 28l-6.5 5A20 20 0 0 0 24 44z\"\n      />\n      <path\n        fill=\"#1976D2\"\n        d=\"M43.6 20.5H42V20H24v8h11.3a12 12 0 0 1-4.1 5.5l6.3 5.3C41 35.7 44 30.3 44 24c0-1.2-.1-2.4-.4-3.5z\"\n      />\n    </svg>\n  );\n}\n\nexport function AppleIcon() {\n  return (\n    <svg\n      width=\"40\"\n      height=\"40\"\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path d=\"M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.08zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z\" />\n    </svg>\n  );\n}\n\nexport function CloseIcon() {\n  return (\n    <svg\n      width=\"24\"\n      height=\"24\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <path\n        d=\"M18 6L6 18M6 6L18 18\"\n        stroke=\"currentColor\"\n        strokeWidth=\"2\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n","import type {\n  CrossdPosition,\n  CrossdPositionDetail,\n  ForgePool,\n  ForgeTokenDetail,\n  GameSwapPool,\n  NetworkStats,\n  RewardPool,\n  StakeInfo,\n  UserDepositInfo,\n  WithdrawPosition,\n} from \"../features/wallet-portfolio/types\";\n\n/**\n * dapp-ui가 렌더하는 outlink의 대분류.\n * `portfolio`는 세부적으로 `origin`으로 더 나뉜다.\n */\nexport type OutlinkCategory =\n  | \"terms\"\n  | \"privacy\"\n  | \"portfolio\"\n  | \"send\"\n  | \"token-detail\";\n\n/**\n * `onOutlink` 콜백에 전달되는 호출 컨텍스트. `category` + `origin`으로\n * 호출측이 목적지별 분기를 할 수 있고, `portfolio-*`의 경우 해당 섹션의\n * 원시 데이터를 `payload`로 내려보낸다.\n *\n * 새로운 포트폴리오 섹션이 추가될 때는 이 union에 variant 하나만 더하면\n * 자동으로 호출측에도 타입이 좁혀져서 전달된다.\n */\nexport type OutlinkContext =\n  | { category: \"terms\"; origin: \"terms\" }\n  | { category: \"privacy\"; origin: \"privacy\" }\n  | {\n      category: \"portfolio\";\n      origin: \"portfolio-rewards\";\n      payload: {\n        pool: RewardPool;\n        userDeposit?: UserDepositInfo;\n      };\n    }\n  | {\n      category: \"portfolio\";\n      origin: \"portfolio-points\";\n      payload: {\n        position: WithdrawPosition;\n      };\n    }\n  | {\n      category: \"portfolio\";\n      origin: \"portfolio-stake\";\n      payload: {\n        stakeInfo?: StakeInfo;\n        networkStats?: NetworkStats;\n      };\n    }\n  | {\n      category: \"portfolio\";\n      origin: \"portfolio-game-swap\";\n      payload: {\n        pool: GameSwapPool;\n      };\n    }\n  | {\n      category: \"portfolio\";\n      origin: \"portfolio-forge\";\n      payload: {\n        pool: ForgePool;\n        tokenDetail?: ForgeTokenDetail;\n      };\n    }\n  | {\n      category: \"portfolio\";\n      origin: \"portfolio-crossd-pool\";\n      payload: {\n        position: CrossdPosition;\n        detail?: CrossdPositionDetail;\n      };\n    }\n  | {\n      category: \"send\";\n      origin: \"send-transaction\";\n      payload: {\n        chainId: number;\n        txHash: `0x${string}`;\n      };\n    }\n  | {\n      // 토큰 상세화면의 외부 링크: Network 익스플로러 / 소셜 링크 / 거래 익스플로러.\n      category: \"token-detail\";\n      origin: \"token-network\" | \"token-social\" | \"token-tx\";\n      payload: {\n        address: string;\n        txHash?: string;\n      };\n    };\n\nexport type OutlinkOrigin = OutlinkContext[\"origin\"];\n\n/**\n * outlink 가로채기 콜백. 반환값 규칙:\n *\n *   - `string`      → 해당 URL로 이동(원본을 변형 가능).\n *   - `null`        → 이동 취소(창도 열리지 않음).\n *   - `undefined`   → 원본 URL로 그대로 이동 (no-op).\n *   - `Promise<...>`→ 위 값 중 하나를 resolve. 비동기 동안 사용자 제스처를\n *                     유지하기 위해 빈 창이 먼저 열린 뒤 URL이 채워진다.\n *\n * middle-click · cmd-click · ctrl-click 등 브라우저 새 탭 단축 동작은\n * 가로채지 않고 원본 `href`로 그대로 열린다.\n */\nexport type OnOutlink = (\n  link: string,\n  ctx: OutlinkContext,\n) => string | null | undefined | Promise<string | null | undefined>;\n\n/**\n * `<a target=\"_blank\">` 클릭 이벤트를 받아서 `onOutlink` 핸들러를 실행하고\n * 최종 URL로 새 창을 연다.\n *\n * - sync 반환: `click` 이벤트 동기 스택 안에서 `window.open(url)` 을 1회만\n *   호출한다. 팝업 차단기는 사용자 제스처 동기 스택 내의 open을 허용하므로\n *   빈 창을 미리 열 필요가 없다. 빈 창을 여는 패턴은 RN WebView 처럼\n *   `window.open` 을 즉시 OS URL 오픈(`Linking.openURL`)으로 매핑하는 환경에서\n *   \"Invalid URL: cannot be empty\" 에러를 유발하므로, sync 경로에서는 반드시\n *   빈 창을 만들지 않는다.\n * - async 반환(Promise): resolve 이후의 `window.open` 은 팝업 차단기가\n *   막을 수 있으므로, 사용자 제스처 안에서 빈 창을 선(先)개방 해두고\n *   resolve 된 URL 을 그 창의 location 에 주입한다.\n *\n * 반환값 없음 — 이벤트에 대한 모든 부수효과를 처리.\n */\nexport function handleOutlinkClick(\n  e: React.MouseEvent<HTMLAnchorElement>,\n  url: string,\n  ctx: OutlinkContext,\n  onOutlink: OnOutlink | undefined,\n): void {\n  // 핸들러가 없으면 anchor 기본 동작을 그대로 둔다.\n  if (!onOutlink) return;\n\n  // middle-click / modified click은 브라우저 기본 탭 열기 UX를 유지한다.\n  // 기본 동작만 가로채는 정책.\n  if (\n    e.button !== 0 ||\n    e.metaKey ||\n    e.ctrlKey ||\n    e.shiftKey ||\n    e.altKey ||\n    e.defaultPrevented\n  ) {\n    return;\n  }\n\n  e.preventDefault();\n\n  let maybe: string | null | undefined | Promise<string | null | undefined>;\n  try {\n    maybe = onOutlink(url, ctx);\n  } catch (err) {\n    console.error(\"[dapp-ui] onOutlink threw:\", err);\n    return;\n  }\n\n  const openInNewTab = (target: string) => {\n    if (typeof window === \"undefined\") return;\n    window.open(target, \"_blank\", \"noopener,noreferrer\");\n  };\n\n  const isPromise = (\n    v: unknown,\n  ): v is Promise<string | null | undefined> =>\n    !!v &&\n    (typeof v === \"object\" || typeof v === \"function\") &&\n    typeof (v as { then?: unknown }).then === \"function\";\n\n  if (isPromise(maybe)) {\n    // Async 경로: popup blocker 회피를 위해 빈 창 선-개방 후 URL 주입.\n    const win =\n      typeof window !== \"undefined\"\n        ? window.open(\"\", \"_blank\", \"noopener,noreferrer\")\n        : null;\n\n    const finalize = (result: string | null | undefined) => {\n      if (result === null) {\n        win?.close();\n        return;\n      }\n      const target = result ?? url;\n      if (win) {\n        try {\n          win.location.href = target;\n        } catch {\n          // 일부 브라우저에서 noopener로 연 창은 location 쓰기가 막힐 수 있음 — 폴백.\n          win.close();\n          openInNewTab(target);\n        }\n      } else {\n        openInNewTab(target);\n      }\n    };\n\n    maybe.then(finalize, (err) => {\n      console.error(\"[dapp-ui] onOutlink rejected:\", err);\n      win?.close();\n    });\n    return;\n  }\n\n  // Sync 경로: 빈 창을 미리 열지 않고, 반환된 URL 로 1회만 window.open.\n  const result = maybe as string | null | undefined;\n  if (result === null) return; // 이동 취소\n  const target = result ?? url;\n  openInNewTab(target);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Drawer } from \"vaul\";\nimport { WalletPortfolioContext } from \"../providers/wallet-portfolio-provider\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport type { Environment, Theme } from \"../../../types\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport type {\n  EstimateGasFn,\n  LpBalanceReaderFn,\n  SendTransactionFn,\n  StakingRewardsReaderFn,\n} from \"../types\";\nimport { WalletPortfolioTrigger } from \"./WalletPortfolioTrigger\";\nimport { WalletPortfolioContent } from \"./WalletPortfolioContent\";\nimport \"../styles/portfolio.css\";\n\nexport interface WalletPortfolioProps {\n  env?: Environment;\n  theme?: Theme;\n  walletAddress: string;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /**\n   * 포트폴리오 섹션 내부 외부 링크 이동을 가로채는 콜백.\n   * 반환값으로 URL 변형 / 취소가 가능하며 async도 지원한다.\n   */\n  onOutlink?: OnOutlink;\n  /**\n   * game-swap LP 섹션이 사용자별 LP 잔고/지분율을 온체인에서 보강하기 위해\n   * host(wagmi 보유 측)로부터 주입받는 reader. 미주입 시 dapp-ui가 CROSS 체인\n   * RPC로 직접 읽는 내부 폴백으로 동작한다.\n   */\n  lpBalanceReader?: LpBalanceReaderFn;\n  /**\n   * CROSS Staking 섹션이 온체인 `earned()` / `balanceOf()`를 보강하기 위해\n   * host(wagmi 보유 측)로부터 주입받는 reader. 미주입 시 API 값으로 폴백한다.\n   */\n  stakingRewardsReader?: StakingRewardsReaderFn;\n  /**\n   * 내장 Withdraw(Rewards) 트랜잭션 서명/브로드캐스트 콜백.\n   * wagmi의 `sendTransactionAsync`를 그대로 전달해도 호환된다.\n   * 미주입 시 Withdraw는 기존 외부 링크로 폴백한다.\n   */\n  sendTransaction?: SendTransactionFn;\n  /** 내장 Withdraw 확인 화면의 가스 추정 콜백. 미주입 시 지갑 추정에 위임. */\n  estimateGas?: EstimateGasFn;\n  /**\n   * 포트폴리오 금액 표시 통화의 USD 환산 비율(USD 기준). 미주입 시 1(=USD).\n   */\n  conversionRatio?: number;\n  /** 포트폴리오 금액 표시 통화 기호. 미주입 시 `\"$\"`. */\n  currencySymbol?: string;\n  children: React.ReactNode;\n}\n\nfunction WalletPortfolioRoot({\n  env,\n  theme = \"light\",\n  walletAddress,\n  open: propOpen,\n  onOpenChange,\n  onOutlink,\n  lpBalanceReader,\n  stakingRewardsReader,\n  sendTransaction,\n  estimateGas,\n  conversionRatio,\n  currencySymbol,\n  children,\n}: WalletPortfolioProps) {\n  const resolvedEnv = resolveEnvironment(env);\n  const isControlled = propOpen !== undefined;\n  const [internalOpen, setInternalOpen] = React.useState(false);\n  const open = isControlled ? propOpen : internalOpen;\n\n  const handleOpenChange = React.useCallback(\n    (next: boolean) => {\n      if (!isControlled) setInternalOpen(next);\n      onOpenChange?.(next);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  const close = React.useCallback(\n    () => handleOpenChange(false),\n    [handleOpenChange],\n  );\n\n  const contextValue = React.useMemo(\n    () => ({\n      env: resolvedEnv,\n      theme,\n      open,\n      walletAddress,\n      close,\n      onOutlink,\n      lpBalanceReader,\n      stakingRewardsReader,\n      sendTransaction,\n      estimateGas,\n      conversionRatio,\n      currencySymbol,\n    }),\n    [\n      resolvedEnv,\n      theme,\n      open,\n      walletAddress,\n      close,\n      onOutlink,\n      lpBalanceReader,\n      stakingRewardsReader,\n      sendTransaction,\n      estimateGas,\n      conversionRatio,\n      currencySymbol,\n    ],\n  );\n\n  return (\n    <WalletPortfolioContext.Provider value={contextValue}>\n      <Drawer.Root\n        direction=\"bottom\"\n        open={open}\n        onOpenChange={handleOpenChange}\n        noBodyStyles\n      >\n        {children}\n      </Drawer.Root>\n    </WalletPortfolioContext.Provider>\n  );\n}\n\nexport const WalletPortfolio = Object.assign(WalletPortfolioRoot, {\n  Trigger: WalletPortfolioTrigger,\n  Content: WalletPortfolioContent,\n});\n","\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { Environment, Theme } from \"../../../types\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport type {\n  EstimateGasFn,\n  LpBalanceReaderFn,\n  SendTransactionFn,\n  StakingRewardsReaderFn,\n} from \"../types\";\nimport { DEFAULT_PORTFOLIO_FIAT, type PortfolioFiat } from \"../utils\";\n\nexport interface WalletPortfolioContextValue {\n  env: Environment;\n  theme: Theme;\n  open: boolean;\n  walletAddress: string;\n  close: () => void;\n  /**\n   * game-swap LP 섹션이 사용자별 LP 잔고/지분율을 온체인에서 보강하기 위해\n   * host(wagmi 보유 측)로부터 주입받는 reader. 미주입 시 dapp-ui가 CROSS 체인\n   * RPC로 직접 읽는 내부 폴백으로 동작한다.\n   */\n  lpBalanceReader?: LpBalanceReaderFn;\n  /**\n   * CROSS Staking 섹션이 온체인 `earned()` / `balanceOf()`를 보강하기 위해\n   * host(wagmi 보유 측)로부터 주입받는 reader. 미주입 시 API\n   * `cumulative_earned` / `total_stake`로 폴백한다.\n   */\n  stakingRewardsReader?: StakingRewardsReaderFn;\n  /**\n   * 포트폴리오 섹션(Rewards / Forge / DEX Order 등) 내부 `<a target=\"_blank\">`\n   * 클릭을 가로채 목적지 URL을 변형하거나 취소할 수 있는 콜백.\n   * `handleOutlinkClick` 유틸을 통해 각 섹션에서 일관되게 호출된다.\n   */\n  onOutlink?: OnOutlink;\n  /**\n   * 내장 Withdraw(Rewards) 등 포트폴리오 안에서 트랜잭션을 직접 보낼 때\n   * 사용하는 서명/브로드캐스트 콜백. host(wagmi 보유 측)의\n   * `sendTransactionAsync`를 그대로 전달해도 호환된다.\n   * 미주입 시 해당 액션은 기존 외부 링크로 폴백한다.\n   */\n  sendTransaction?: SendTransactionFn;\n  /** 내장 Withdraw 확인 화면의 가스 추정 콜백. 미주입 시 지갑 추정에 위임. */\n  estimateGas?: EstimateGasFn;\n  /**\n   * 포트폴리오 금액 표시 통화의 USD 환산 비율(USD 기준). 예) USD=1, EUR=0.92.\n   * 미주입/비유효(<=0) 시 1(=USD 원 금액)로 취급한다.\n   */\n  conversionRatio?: number;\n  /** 포트폴리오 금액 표시 통화 기호. 미주입 시 `\"$\"`. */\n  currencySymbol?: string;\n}\n\nexport const WalletPortfolioContext =\n  createContext<WalletPortfolioContextValue | null>(null);\n\nexport function useWalletPortfolioContext() {\n  const ctx = useContext(WalletPortfolioContext);\n  if (!ctx) {\n    throw new Error(\n      \"WalletPortfolio compound components must be used within <WalletPortfolio>\",\n    );\n  }\n  return ctx;\n}\n\n/**\n * 컨텍스트의 `conversionRatio`/`currencySymbol`를 금액 포맷 유틸이 쓰는\n * {@link PortfolioFiat} 형태로 정규화해 반환한다. 미주입 시 USD 기본값.\n */\nexport function usePortfolioFiat(): PortfolioFiat {\n  const { conversionRatio, currencySymbol } = useWalletPortfolioContext();\n  // rate/symbol이 바뀔 때만 새 객체를 만들어, 소비 측 useMemo/의존성이\n  // 매 렌더 무효화되지 않도록 참조 안정성을 유지한다.\n  return useMemo<PortfolioFiat>(\n    () => ({\n      rate:\n        typeof conversionRatio === \"number\" && conversionRatio > 0\n          ? conversionRatio\n          : DEFAULT_PORTFOLIO_FIAT.rate,\n      symbol: currencySymbol || DEFAULT_PORTFOLIO_FIAT.symbol,\n    }),\n    [conversionRatio, currencySymbol],\n  );\n}\n","import BigNumber from \"bignumber.js\";\nimport type { Environment } from \"../../types\";\nimport type { StakeRewardRecord, WithdrawPosition } from \"./types\";\n\nconst ISO_DATE_PREFIX_RE = /^(\\d{4}-\\d{2}-\\d{2})/;\n\n/**\n * posa 포트폴리오의 `pickChartAccrued`와 동일한 개인 실현 APR 분자 선택 로직.\n *\n * `/rewards` 레코드를 `reference_date`의 `YYYY-MM-DD` prefix로 일자 버킷팅한 뒤,\n * 오늘 버킷의 `cumulative_earned`(없으면 최신일)를 골라 돌려준다. APR 분자로\n * 이 값을 쓰면, 변동이 큰 온체인 실시간 추정치(earned()) 대신 안정적인 차트\n * 누적값을 사용하게 되어 posa 포트폴리오 My APR과 동일하게 정렬된다.\n *\n * tz offset이 버킷 경계를 밀지 않도록 Date 변환 없이 문자열 prefix를 그대로\n * 버킷 키로 쓴다(posa와 동일). `reference_date`가 없는 레코드는 제외한다.\n * 레코드가 없으면 undefined.\n */\nexport function pickAccruedCumulativeEarned(\n  records: StakeRewardRecord[] | undefined,\n): string | undefined {\n  if (!records || records.length === 0) return undefined;\n\n  // reference_date prefix -> 해당 일자 버킷의 cumulative_earned.\n  // 오름차순(stable) 순회로 set 하여 posa의 bucketByDay와 동일하게 마지막 기록이\n  // 남는다. 일자별 스냅샷은 보통 1건이라 순서 영향은 없다.\n  const byDay = new Map<string, string>();\n  let latestKey: string | undefined;\n  for (const { key, cumulative } of records\n    .map((r) => ({\n      key: ISO_DATE_PREFIX_RE.exec(r.reference_date)?.[1],\n      cumulative: r.cumulative_earned,\n    }))\n    .filter(\n      (x): x is { key: string; cumulative: string } =>\n        !!x.key && x.cumulative != null,\n    )\n    .sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))) {\n    byDay.set(key, cumulative);\n    latestKey = key;\n  }\n  if (latestKey === undefined) return undefined;\n\n  const today = new Date().toISOString().slice(0, 10);\n  return byDay.get(today) ?? byDay.get(latestKey);\n}\n\nexport const PORTFOLIO_ESTIMATE_TOOLTIP = {\n  included: [\n    \"ONErewards deposited assets\",\n    \"ONErewards claimable rewards\",\n  ],\n  excluded: [\n    \"ONEforge LP value\",\n    \"ONEstaking value\",\n    \"Gametoken LP value\",\n    \"Wallet balances outside Portfolio\",\n  ],\n} as const;\n\nexport function shouldShowPortfolioEstimateTooltip(env: Environment): boolean {\n  return env !== \"production\";\n}\n\n/**\n * ONEpoints에는 ONErewards의 활성 풀을 노출하지 않는다.\n * `useWithdrawPositions`에서 reward pools API에 없는 quest 풀만 `Unknown`으로\n * 표시하므로, Points 섹션은 이 포지션만 소유한다.\n */\nexport function isPointsWithdrawPosition(\n  position: Pick<WithdrawPosition, \"poolType\">,\n): boolean {\n  return position.poolType === \"Unknown\";\n}\n\nexport function formatWalletAddress(address: string): string {\n  if (address.length <= 12) return address;\n  return `${address.slice(0, 6)}...${address.slice(-4)}`;\n}\n\nexport function shortenTransactionHash(hash: string): string {\n  if (hash.length <= 13) return hash;\n  return `${hash.slice(0, 6)}...${hash.slice(-4)}`;\n}\n\n/**\n * Withdraw 다이얼로그 reward 카드에서 심볼+금액이 길 때 폰트를 줄여 전부\n * 보이게 하는 축소 단계. undefined면 기본 크기.\n *\n * 임계값은 `.wp-claim-reward-card`의 좌우 패딩 8px(카드 폭 최대 활용) 기준 —\n * 심볼 7자 + 소수 18자리 금액(예: tONEUSD 20.421215840522445067)까지는\n * sm(16px)으로 한 줄에 다 보이고, 그보다 길 때만 xs로 내려간다.\n */\nexport function rewardCardFitLevel(\n  symbol: string,\n  amount: string,\n): \"sm\" | \"xs\" | undefined {\n  const len = symbol.length + amount.length;\n  if (len > 32) return \"xs\";\n  if (len > 24) return \"sm\";\n  return undefined;\n}\n\n/**\n * crossd 표기 관례: WONE(wrapped native)는 화면에는 \"ONE\"으로 표시한다\n * (crossd-frontend `displayTokenSymbol`과 동일). 트랜잭션 calldata는 원래\n * WONE 주소를 그대로 쓰므로 표시 전용 변환이다.\n */\nexport function displayCrossdSymbol(symbol?: string): string | undefined {\n  return symbol?.toUpperCase() === \"WONE\" ? \"ONE\" : symbol;\n}\n\n/** viem/지갑 에러 메시지에서 요약 뒤에 붙는 기술 덤프 시작 마커. */\nconst WALLET_ERROR_CUT_MARKERS = [\n  \"Request Arguments:\",\n  \"Details:\",\n  \"Docs:\",\n  \"Version:\",\n  \"Contract Call:\",\n];\n\n/**\n * 지갑/RPC 에러를 사용자 표시용 한 줄 요약으로 줄인다. 원문(Request\n * Arguments, calldata 덤프 등)은 UI에 노출하지 않고 console.error로만\n * 남긴다. 요약을 못 뽑으면 fallback 반환.\n */\nexport function toDisplayWalletError(err: unknown, fallback: string): string {\n  console.error(\"[wallet-portfolio] withdraw failed:\", err);\n  const message = err instanceof Error ? err.message : \"\";\n  let summary = message;\n  for (const marker of WALLET_ERROR_CUT_MARKERS) {\n    const idx = summary.indexOf(marker);\n    if (idx >= 0) summary = summary.slice(0, idx);\n  }\n  const firstLine = summary\n    .split(\"\\n\")\n    .map((line) => line.trim())\n    .find((line) => line.length > 0);\n  return firstLine || fallback;\n}\n\n/** `withdrawERC20(uint256 poolId, uint256 amount)` 함수 selector. */\nconst WITHDRAW_ERC20_SELECTOR = \"0xe00e8bf3\";\n\n/**\n * `CrossGameRewardRouter.withdrawERC20(poolId, amount)` 호출 calldata 인코딩.\n * keccak 의존성 없이 selector를 상수로 두는 `encodeErc20Transfer`(send)와\n * 동일한 방식. amount는 raw(wei) BigInt.\n */\nexport function encodeWithdrawErc20(\n  poolId: number | bigint,\n  amount: bigint,\n): `0x${string}` {\n  const id = typeof poolId === \"bigint\" ? poolId : BigInt(poolId);\n  if (id < 0n) throw new Error(\"poolId must be non-negative\");\n  if (amount < 0n) throw new Error(\"amount must be non-negative\");\n\n  const poolIdWord = id.toString(16).padStart(64, \"0\");\n  const amountWord = amount.toString(16).padStart(64, \"0\");\n  return `${WITHDRAW_ERC20_SELECTOR}${poolIdWord}${amountWord}` as `0x${string}`;\n}\n\n/** wei 값을 Gwei 소수 표기로 변환 (가스 상세 행 표기용). */\nexport function formatGweiFromWei(value?: bigint): string {\n  if (!value || value <= 0n) return \"0\";\n  return new BigNumber(value.toString())\n    .shiftedBy(-9)\n    .toFormat(2, BigNumber.ROUND_DOWN)\n    .replace(/(\\.\\d*?[1-9])0+$/, \"$1\")\n    .replace(/\\.0+$/, \"\");\n}\n\nexport function formatGasLimit(value?: bigint): string {\n  if (!value || value <= 0n) return \"0\";\n  return new BigNumber(value.toString()).toFormat(0, BigNumber.ROUND_DOWN);\n}\n\n/**\n * 예상 수수료(native, wei) 표기. 소액이면 7자리, 그 외 4자리 절사 후\n * 트레일링 0 제거.\n */\nexport function formatNativeFeeFromWei(value?: bigint): string {\n  if (!value || value <= 0n) return \"0\";\n  const amount = new BigNumber(value.toString()).shiftedBy(-18);\n  const precision = amount.isLessThan(0.0001) ? 7 : 4;\n  return amount\n    .toFormat(precision, BigNumber.ROUND_DOWN)\n    .replace(/(\\.\\d*?[1-9])0+$/, \"$1\")\n    .replace(/\\.0+$/, \"\");\n}\n\nexport function formatPortfolioTokenAmount(\n  raw: string,\n  decimals: number,\n): string {\n  const amount = new BigNumber(raw).shiftedBy(-decimals);\n  if (!amount.isFinite() || amount.isZero()) return \"0\";\n  return amount.isGreaterThanOrEqualTo(1)\n    ? amount.toFormat(2, BigNumber.ROUND_DOWN)\n    : amount.toFormat(4, BigNumber.ROUND_DOWN);\n}\n\n/** 이미 토큰 단위(십진 문자열)로 내려온 금액의 표기용 포맷. wei 변환 없음. */\nexport function formatPortfolioDecimalAmount(value: string): string {\n  return formatPortfolioTokenAmount(value, 0);\n}\n\n/**\n * 포트폴리오 금액 표시 통화 설정.\n *\n * - `rate`: USD → 표시 통화 환산 비율(USD 기준). 예) USD=1, EUR=0.92.\n *   host(웹뷰 등)가 선택 통화 비율을 주입한다. 미주입/비유효 시 1(=USD).\n * - `symbol`: 표시 통화 기호. 미주입 시 `\"$\"`.\n */\nexport interface PortfolioFiat {\n  rate: number;\n  symbol: string;\n}\n\n/** 통화 미주입 환경의 기본값(USD). */\nexport const DEFAULT_PORTFOLIO_FIAT: PortfolioFiat = { rate: 1, symbol: \"$\" };\n\n/**\n * USD BigNumber를 표시 통화로 환산·포맷한다. 소수 2자리 floor로,\n * 기존 USD 표기 규칙(`§4 Display = floor`)을 그대로 유지한다.\n * `rate`가 비유효(<=0/NaN)면 1로 취급해 원 금액을 표기한다.\n */\nexport function formatFiatFromUsd(\n  usd: BigNumber,\n  fiat: PortfolioFiat = DEFAULT_PORTFOLIO_FIAT,\n): string {\n  const rate =\n    Number.isFinite(fiat.rate) && fiat.rate > 0 ? fiat.rate : 1;\n  const symbol = fiat.symbol || \"$\";\n  return `${symbol}${usd.times(rate).toFormat(2, BigNumber.ROUND_DOWN)}`;\n}\n\n/**\n * 포트폴리오 금액의 표시 통화 환산 표기. `amount(raw, decimals) × price`(USD)를\n * `fiat`로 환산해 소수 2자리 절사(floor)로 표기한다.\n *\n * - price가 0/비유효(단가 미상)면 `null` → 호출부가 환산가 줄을 숨긴다.\n *   (단가를 모르면서 \"$0.00\"으로 오인시키지 않기 위함.)\n * - price는 알지만 금액이 0이면 \"0.00\"(기호 포함)을 그대로 반환한다.\n * - `fiat` 미주입 시 USD(`$`, rate 1)로 폴백한다.\n */\nexport function formatPortfolioUsd(\n  raw: string,\n  decimals: number,\n  price: string,\n  fiat: PortfolioFiat = DEFAULT_PORTFOLIO_FIAT,\n): string | null {\n  const p = new BigNumber(price || \"0\");\n  if (!p.isFinite() || p.isLessThanOrEqualTo(0)) return null;\n  const usd = new BigNumber(raw || \"0\").shiftedBy(-decimals).times(p);\n  if (!usd.isFinite()) return null;\n  return formatFiatFromUsd(usd, fiat);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Drawer } from \"vaul\";\nimport { useWalletPortfolioContext } from \"../providers/wallet-portfolio-provider\";\nimport { shortenAddress } from \"../../../lib/format\";\n\nexport interface WalletPortfolioTriggerProps {\n  asChild?: boolean;\n  children?: React.ReactNode;\n}\n\nexport function WalletPortfolioTrigger({\n  asChild,\n  children,\n}: WalletPortfolioTriggerProps) {\n  const { walletAddress } = useWalletPortfolioContext();\n\n  const hasChildren = children != null;\n  const shouldAsChild = asChild ?? true;\n\n  const triggerContent = hasChildren ? (\n    children\n  ) : (\n    <button type=\"button\">{shortenAddress(walletAddress)}</button>\n  );\n\n  return (\n    <Drawer.Trigger asChild={shouldAsChild}>{triggerContent}</Drawer.Trigger>\n  );\n}\n","\"use client\";\n\nimport { Drawer } from \"vaul\";\nimport { useWalletPortfolioContext } from \"../providers/wallet-portfolio-provider\";\nimport { cn } from \"../../../lib/utils\";\nimport { WalletPortfolioBody } from \"./WalletPortfolioBody\";\n\nexport interface WalletPortfolioContentProps {\n  className?: string;\n}\n\nexport function WalletPortfolioContent({\n  className,\n}: WalletPortfolioContentProps) {\n  const {\n    env,\n    theme,\n    walletAddress,\n    close,\n    onOutlink,\n    lpBalanceReader,\n    stakingRewardsReader,\n  } = useWalletPortfolioContext();\n\n  return (\n    <Drawer.Portal>\n      <Drawer.Overlay className=\"wp-overlay\" />\n      <Drawer.Content\n        className={cn(\"wp-fullscreen\", className)}\n        data-theme={theme}\n      >\n        <Drawer.Title className=\"wp-sr-only\">Portfolio</Drawer.Title>\n        <WalletPortfolioBody\n          env={env}\n          theme={theme}\n          walletAddress={walletAddress}\n          onBack={close}\n          variant=\"none\"\n          onOutlink={onOutlink}\n          lpBalanceReader={lpBalanceReader}\n          stakingRewardsReader={stakingRewardsReader}\n        />\n      </Drawer.Content>\n    </Drawer.Portal>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { WalletPortfolioContext } from \"../providers/wallet-portfolio-provider\";\nimport {\n  SectionStatusContext,\n  type SectionStatus,\n} from \"../providers/section-status-provider\";\nimport type { Environment, Theme } from \"../../../types\";\nimport type { OnOutlink } from \"../../../lib/outlink\";\nimport { cn } from \"../../../lib/utils\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport {\n  BackArrowIcon,\n  RefreshIcon,\n} from \"./icons\";\nimport { CopyIcon, CheckIcon } from \"../../wallet-info/components/icons\";\nimport { getPortraitUrl } from \"../../wallet-info/constants\";\nimport { formatWalletAddress } from \"../utils\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport { RewardsSection } from \"./sections/RewardsSection\";\nimport { PointsSection } from \"./sections/PointsSection\";\nimport { StakeSection } from \"./sections/StakeSection\";\nimport { GameSwapSection } from \"./sections/GameSwapSection\";\nimport { ForgeSection } from \"./sections/ForgeSection\";\nimport { CrossdPoolSection } from \"./sections/CrossdPoolSection\";\nimport {\n  PORTFOLIO_SECTIONS,\n  type EstimateGasFn,\n  type LpBalanceReaderFn,\n  type PortfolioSection,\n  type SendTransactionFn,\n  type StakingRewardsReaderFn,\n} from \"../types\";\nimport { invalidateWalletPortfolioQueries } from \"../refresh\";\nimport \"../styles/portfolio.css\";\n\nconst POLL_SEC = 15;\n\nexport interface WalletPortfolioBodyProps {\n  env?: Environment;\n  theme?: Theme;\n  walletAddress: string;\n  /**\n   * 헤더 좌측 아바타 옆에 표시할 지갑 이름 (예: \"Account 1\").\n   * 전달되지 않으면 텍스트는 숨겨지고 아바타만 표시된다.\n   */\n  walletName?: string;\n  /** 왼쪽 상단 back 버튼 클릭 핸들러. 없으면 back 버튼 숨김. */\n  onBack?: () => void;\n  /** 헤더 노출 여부. embed 시 외부 헤더를 쓰면 false로 숨길 수 있음. */\n  showHeader?: boolean;\n  /**\n   * 내장 15초 자동 리프레시 폴링 여부 (기본 true).\n   * host가 자체 헤더(리프레시 버튼/폴링)를 소유할 때 false로 끄고,\n   * `invalidateWalletPortfolioQueries(queryClient)`로 직접 갱신한다.\n   */\n  autoRefresh?: boolean;\n  /**\n   * 컨테이너 variant.\n   * - `\"embed\"`(기본): 부모 영역을 채우도록 `.wp-embed` 래퍼로 감쌈\n   * - `\"fullscreen\"`: 고정 풀스크린 `.wp-fullscreen` 래퍼로 감쌈\n   * - `\"none\"`: 래퍼 div 없이 본문만 반환 (Drawer.Content 등 외부 래퍼에 클래스를 직접 줄 때)\n   */\n  variant?: \"fullscreen\" | \"embed\" | \"none\";\n  className?: string;\n  /**\n   * 포트폴리오 섹션 내부 외부 링크 이동을 가로채는 콜백.\n   * 반환값으로 URL 변형 / 취소가 가능하며 async도 지원한다.\n   */\n  onOutlink?: OnOutlink;\n  /**\n   * game-swap LP 섹션이 사용자별 LP 잔고/지분율을 온체인에서 보강하기 위해\n   * host(wagmi 보유 측)로부터 주입받는 reader. 미주입 시 풀 정보만 표시한다.\n   */\n  lpBalanceReader?: LpBalanceReaderFn;\n  /**\n   * CROSS Staking 섹션이 온체인 `earned()` / `balanceOf()`를 보강하기 위해\n   * host(wagmi 보유 측)로부터 주입받는 reader. 미주입 시 API 값으로 폴백한다.\n   */\n  stakingRewardsReader?: StakingRewardsReaderFn;\n  /**\n   * 내장 Withdraw(Rewards) 트랜잭션 서명/브로드캐스트 콜백.\n   * wagmi의 `sendTransactionAsync`를 그대로 전달해도 호환된다.\n   * 미주입 시 Withdraw는 기존 외부 링크로 폴백한다.\n   */\n  sendTransaction?: SendTransactionFn;\n  /** 내장 Withdraw 확인 화면의 가스 추정 콜백. 미주입 시 지갑 추정에 위임. */\n  estimateGas?: EstimateGasFn;\n  /**\n   * 포트폴리오 금액 표시 통화의 USD 환산 비율(USD 기준). 미주입 시 1(=USD).\n   */\n  conversionRatio?: number;\n  /** 포트폴리오 금액 표시 통화 기호. 미주입 시 `\"$\"`. */\n  currencySymbol?: string;\n  /**\n   * 노출할 포트폴리오 섹션을 제한한다. 미지정(`undefined`)이면 모든 섹션을\n   * 기본 순서대로 표시하고, 배열을 주면 포함된 섹션만 (기본 순서를 유지한 채)\n   * 표시한다. 빈 배열이면 아무 섹션도 표시하지 않는다.\n   */\n  sections?: PortfolioSection[];\n}\n\nexport function WalletPortfolioBody({\n  env,\n  theme = \"light\",\n  walletAddress,\n  walletName,\n  onBack,\n  showHeader = true,\n  autoRefresh = true,\n  variant = \"embed\",\n  className,\n  onOutlink,\n  lpBalanceReader,\n  stakingRewardsReader,\n  sendTransaction,\n  estimateGas,\n  conversionRatio,\n  currencySymbol,\n  sections,\n}: WalletPortfolioBodyProps) {\n  const resolvedEnv = resolveEnvironment(env);\n  // 미지정이면 전체 노출, 배열이면 포함된 섹션만 노출(빈 배열 = 전부 숨김).\n  const isVisible = React.useCallback(\n    (section: PortfolioSection) => !sections || sections.includes(section),\n    [sections],\n  );\n  const hasAddress = !!walletAddress;\n  const queryClient = useQueryClient();\n\n  // 각 섹션이 보고한 로딩/보유 상태. 노출 대상 섹션이 전부 \"자산 없음\"으로\n  // 확정되면 전역 빈 화면을, 아직 아무것도 확정되지 않았으면 로더를 그린다.\n  const [sectionStatus, setSectionStatus] = React.useState<\n    Partial<Record<PortfolioSection, SectionStatus>>\n  >({});\n\n  const reportSectionStatus = React.useCallback(\n    (key: PortfolioSection, status: SectionStatus) => {\n      setSectionStatus((prev) => {\n        const cur = prev[key];\n        if (cur && cur.loading === status.loading && cur.empty === status.empty) {\n          return prev;\n        }\n        return { ...prev, [key]: status };\n      });\n    },\n    [],\n  );\n\n  const unregisterSection = React.useCallback((key: PortfolioSection) => {\n    setSectionStatus((prev) => {\n      if (!(key in prev)) return prev;\n      const next = { ...prev };\n      delete next[key];\n      return next;\n    });\n  }, []);\n\n  const sectionStatusContext = React.useMemo(\n    () => ({ report: reportSectionStatus, unregister: unregisterSection }),\n    [reportSectionStatus, unregisterSection],\n  );\n\n  const [countdown, setCountdown] = React.useState(POLL_SEC);\n  const [spinning, setSpinning] = React.useState(false);\n  const [copied, setCopied] = React.useState(false);\n  const copyTimerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n\n  const invalidateAll = React.useCallback(() => {\n    invalidateWalletPortfolioQueries(queryClient);\n  }, [queryClient]);\n\n  const triggerRefresh = React.useCallback(() => {\n    invalidateAll();\n    setSpinning(true);\n    setTimeout(() => setSpinning(false), 600);\n    setCountdown(POLL_SEC);\n  }, [invalidateAll]);\n\n  React.useEffect(() => {\n    if (!hasAddress || !autoRefresh) return;\n    const id = setInterval(() => {\n      setCountdown((prev) => {\n        if (prev <= 1) {\n          triggerRefresh();\n          return POLL_SEC;\n        }\n        return prev - 1;\n      });\n    }, 1_000);\n    return () => clearInterval(id);\n  }, [hasAddress, autoRefresh, triggerRefresh]);\n\n  const close = React.useCallback(() => {\n    onBack?.();\n  }, [onBack]);\n\n  const portraitUrl = hasAddress\n    ? getPortraitUrl(resolvedEnv, walletAddress, { size: 64 })\n    : undefined;\n\n  const handleCopyAddress = React.useCallback(async () => {\n    if (copied || !walletAddress) return;\n    try {\n      await navigator.clipboard.writeText(walletAddress);\n      setCopied(true);\n      clearTimeout(copyTimerRef.current);\n      copyTimerRef.current = setTimeout(() => setCopied(false), 2000);\n    } catch {\n      /* clipboard 미지원/거부 시 무시 */\n    }\n  }, [walletAddress, copied]);\n\n  React.useEffect(() => () => clearTimeout(copyTimerRef.current), []);\n\n  const contextValue = React.useMemo(\n    () => ({\n      env: resolvedEnv,\n      theme,\n      open: true,\n      walletAddress,\n      close,\n      onOutlink,\n      lpBalanceReader,\n      stakingRewardsReader,\n      sendTransaction,\n      estimateGas,\n      conversionRatio,\n      currencySymbol,\n    }),\n    [\n      resolvedEnv,\n      theme,\n      walletAddress,\n      close,\n      onOutlink,\n      lpBalanceReader,\n      stakingRewardsReader,\n      sendTransaction,\n      estimateGas,\n      conversionRatio,\n      currencySymbol,\n    ],\n  );\n\n  // 노출 대상 섹션 전체가 로딩을 끝내고 \"자산 없음\"으로 확정된 경우에만\n  // 전역 빈 화면을 그린다. 하나라도 아직 로딩 중이면(내용도 없으면) 로더 표시.\n  const expectedSections = PORTFOLIO_SECTIONS.filter(isVisible);\n  const allSettled =\n    expectedSections.length > 0 &&\n    expectedSections.every((key) => {\n      const status = sectionStatus[key];\n      return !!status && !status.loading;\n    });\n  const allEmpty =\n    allSettled && expectedSections.every((key) => sectionStatus[key]?.empty);\n  const anyContent = expectedSections.some((key) => {\n    const status = sectionStatus[key];\n    return !!status && !status.empty;\n  });\n  const showLoader =\n    expectedSections.length > 0 && !allSettled && !anyContent;\n\n  const inner = (\n    <>\n      {showHeader && (\n        <div className=\"wp-header\">\n          <div className=\"wp-header-account\">\n            {onBack && (\n              <button\n                type=\"button\"\n                className=\"wp-header-btn\"\n                aria-label=\"Back\"\n                data-track=\"back\"\n                onClick={onBack}\n              >\n                <BackArrowIcon />\n              </button>\n            )}\n            <span className=\"wp-header-avatar\" aria-hidden>\n              {portraitUrl && (\n                <img\n                  src={portraitUrl}\n                  alt=\"\"\n                  className=\"wp-header-avatar-img\"\n                  draggable={false}\n                />\n              )}\n            </span>\n            {(walletName || walletAddress) && (\n              <div className=\"wp-header-account-meta\">\n                {walletName && (\n                  <span className=\"wp-header-wallet-name\" title={walletName}>\n                    {walletName}\n                  </span>\n                )}\n                {walletAddress && (\n                  <button\n                    type=\"button\"\n                    className=\"wp-header-address-btn\"\n                    onClick={handleCopyAddress}\n                    aria-label=\"Copy wallet address\"\n                    data-track=\"address_copy\"\n                  >\n                    <span className=\"wp-header-address\">\n                      {formatWalletAddress(walletAddress)}\n                    </span>\n                    <span\n                      className=\"wp-header-address-copy\"\n                      data-copied={copied}\n                    >\n                      {copied ? (\n                        <CheckIcon size={14} />\n                      ) : (\n                        <CopyIcon size={14} />\n                      )}\n                    </span>\n                  </button>\n                )}\n              </div>\n            )}\n          </div>\n          <button\n            type=\"button\"\n            className=\"wp-header-btn wp-refresh-btn\"\n            aria-label=\"Refresh portfolio\"\n            data-track=\"refresh\"\n            onClick={triggerRefresh}\n          >\n            <RefreshIcon className={spinning ? \"wp-spin-once\" : undefined} />\n            {hasAddress && (\n              <span className=\"wp-refresh-badge\">{countdown}</span>\n            )}\n          </button>\n        </div>\n      )}\n\n      <div className=\"wp-body\">\n        <div className=\"wp-body-inner\" data-empty={allEmpty || undefined}>\n          {isVisible(\"rewards\") && <RewardsSection />}\n          {isVisible(\"points\") && <PointsSection />}\n          {isVisible(\"staking\") && <StakeSection />}\n          {isVisible(\"gameSwap\") && <GameSwapSection />}\n          {isVisible(\"forge\") && <ForgeSection />}\n          {isVisible(\"crossdPool\") && <CrossdPoolSection />}\n          {showLoader && (\n            <div className=\"wp-portfolio-loading\" role=\"status\" aria-label=\"Loading portfolio\">\n              <span className=\"wp-portfolio-spinner\" aria-hidden />\n            </div>\n          )}\n          {allEmpty && (\n            <div className=\"wp-portfolio-empty\" role=\"status\">\n              <span className=\"wp-portfolio-empty-icon\" aria-hidden>\n                <svg\n                  width=\"28\"\n                  height=\"28\"\n                  viewBox=\"0 0 24 24\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"1.6\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                >\n                  <path d=\"M20 7H5a2 2 0 0 1-2-2 2 2 0 0 1 2-2h13v4\" />\n                  <path d=\"M3 5v13a2 2 0 0 0 2 2h15a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1\" />\n                  <circle cx=\"16.5\" cy=\"13.5\" r=\"1\" fill=\"currentColor\" stroke=\"none\" />\n                </svg>\n              </span>\n              <p className=\"wp-portfolio-empty-title\">No assets yet</p>\n              <p className=\"wp-portfolio-empty-desc\">\n                Assets you hold across ONE services will appear here.\n              </p>\n            </div>\n          )}\n        </div>\n      </div>\n    </>\n  );\n\n  return (\n    <WalletPortfolioContext.Provider value={contextValue}>\n      <SectionStatusContext.Provider value={sectionStatusContext}>\n        <TrackingBoundary feature=\"wallet_portfolio\">\n          {variant === \"none\" ? (\n            inner\n          ) : (\n            <div\n              className={cn(\n                variant === \"fullscreen\" ? \"wp-fullscreen\" : \"wp-embed\",\n                className,\n              )}\n              data-theme={theme}\n            >\n              {inner}\n            </div>\n          )}\n        </TrackingBoundary>\n      </SectionStatusContext.Provider>\n    </WalletPortfolioContext.Provider>\n  );\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { PortfolioSection } from \"../types\";\n\n/** 섹션 하나의 데이터 상태. 최초 로딩 중에는 empty를 신뢰할 수 없다. */\nexport interface SectionStatus {\n  loading: boolean;\n  empty: boolean;\n}\n\nexport interface SectionStatusContextValue {\n  /** 섹션이 자신의 로딩/보유 상태를 부모(WalletPortfolioBody)에 보고한다. */\n  report: (key: PortfolioSection, status: SectionStatus) => void;\n  /** 섹션 언마운트 시 보고를 철회한다. */\n  unregister: (key: PortfolioSection) => void;\n}\n\n/**\n * 각 섹션이 \"보유 자산 없음\" 여부를 부모에 보고하기 위한 컨텍스트.\n * - 섹션 카드: 자산이 없으면 스스로 숨는다(SectionCard가 null 반환).\n * - 부모: 노출 대상 섹션이 전부 empty로 확정되면 전역 빈 화면을 그린다.\n * Provider 밖에서 쓰이면(스토리북/단독 렌더) no-op으로 동작한다.\n */\nexport const SectionStatusContext =\n  createContext<SectionStatusContextValue | null>(null);\n\nexport function useSectionStatusReporter() {\n  return useContext(SectionStatusContext);\n}\n","interface IconProps {\n  size?: number;\n  className?: string;\n}\n\nexport function BackArrowIcon({ size = 22, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 22 22\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M14 4L7 11L14 18\"\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function RefreshIcon({ size = 22, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 22 22\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M18.333 3.667v5.5h-5.5\"\n        stroke=\"currentColor\"\n        strokeWidth={1.833}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M16.17 13.75a7.333 7.333 0 11-1.54-7.59L18.333 9.167\"\n        stroke=\"currentColor\"\n        strokeWidth={1.833}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function CloseIcon({ size = 24, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M18 6L6 18M6 6l12 12\"\n        stroke=\"currentColor\"\n        strokeWidth={1.5}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function EyeIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M1.333 8s2.424-4.667 6.667-4.667S14.667 8 14.667 8 12.243 12.667 8 12.667 1.333 8 1.333 8z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M8 10a2 2 0 100-4 2 2 0 000 4z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function InfoCircleIcon({ size = 12, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 12 12\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M6 11A5 5 0 106 1a5 5 0 000 10zM6 8V6M6 4h.005\"\n        stroke=\"currentColor\"\n        strokeWidth={1}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ClipboardIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M5.333 5.333V4a1.333 1.333 0 011.334-1.333h5.333A1.333 1.333 0 0113.333 4v5.333a1.333 1.333 0 01-1.333 1.334h-1.333\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M4 5.333h5.333c.737 0 1.334.597 1.334 1.334V12c0 .736-.597 1.333-1.334 1.333H4A1.333 1.333 0 012.667 12V6.667C2.667 5.93 3.263 5.333 4 5.333z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function CheckIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M13.333 4L6 11.333 2.667 8\"\n        stroke=\"currentColor\"\n        strokeWidth={1.667}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function SuccessCheckIcon({ size = 48, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 48 48\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <circle cx=\"24\" cy=\"24\" r=\"24\" fill=\"currentColor\" />\n      <path\n        d=\"M14 24.5l6.6 6.6L34.5 17.5\"\n        stroke=\"var(--wp-claim-success-mark, #ffffff)\"\n        strokeWidth={4}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ChevronDownIcon({ size = 6, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={(size * 4) / 6}\n      viewBox=\"0 0 6 4\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M1 1l2 2 2-2\"\n        stroke=\"currentColor\"\n        strokeWidth={1}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ExternalLinkIcon({ size = 12, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 12 12\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M5 2H2.5A1.5 1.5 0 0 0 1 3.5V9.5A1.5 1.5 0 0 0 2.5 11H8.5A1.5 1.5 0 0 0 10 9.5V7\"\n        stroke=\"currentColor\"\n        strokeWidth={1.2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M7 1H11V5\"\n        stroke=\"currentColor\"\n        strokeWidth={1.2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M11 1L6 6\"\n        stroke=\"currentColor\"\n        strokeWidth={1.2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { RewardPool, RewardPoolsResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const REWARD_POOLS_QUERY_KEY = \"wp:reward-pools\";\n\nexport function useRewardPools(env: Environment, enabled = true) {\n  const { data, ...rest } = useQuery<RewardPoolsResponse>({\n    queryKey: [REWARD_POOLS_QUERY_KEY, env],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"gameReward\", env);\n      const res = await fetch(`${baseUrl}/v1/pools`, { cache: \"no-store\", signal });\n      if (!res.ok) throw new Error(`Failed to fetch pools: ${res.status}`);\n      const json: RewardPoolsResponse = await res.json();\n      if (json.code !== 200) throw new Error(`Pools API error: ${json.code}`);\n      return json;\n    },\n    enabled,\n    staleTime: 0,\n  });\n\n  const crossPools = useMemo<RewardPool[]>(\n    () => data?.data.pools.filter((p) => p.pool_type === \"CrossPool\") ?? [],\n    [data],\n  );\n\n  const gamePools = useMemo<RewardPool[]>(\n    () => data?.data.pools.filter((p) => p.pool_type === \"GamePool\") ?? [],\n    [data],\n  );\n\n  return { crossPools, gamePools, allPools: data?.data.pools ?? [], ...rest };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { UserDepositInfo, UserDepositsResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const USER_DEPOSITS_QUERY_KEY = \"wp:user-deposits\";\n\nexport function useUserDeposits(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const isEnabled = enabled && !!walletAddress;\n\n  const { data, ...rest } = useQuery<UserDepositsResponse>({\n    queryKey: [USER_DEPOSITS_QUERY_KEY, env, walletAddress],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"gameReward\", env);\n      const res = await fetch(\n        `${baseUrl}/v1/users/${walletAddress}/deposits`,\n        { cache: \"no-store\", signal },\n      );\n      if (!res.ok) throw new Error(`Deposits fetch failed: ${res.status}`);\n      const json: UserDepositsResponse = await res.json();\n      if (json.code !== 200) throw new Error(`Deposits API error: ${json.code}`);\n      return json;\n    },\n    enabled: isEnabled,\n    staleTime: 0,\n  });\n\n  const depositMap = useMemo(() => {\n    const map = new Map<string, UserDepositInfo>();\n    if (!data?.data.user_deposits) return map;\n    for (const deposit of data.data.user_deposits) {\n      map.set(deposit.pool_address.toLowerCase(), deposit);\n    }\n    return map;\n  }, [data]);\n\n  return { depositMap, deposits: data?.data.user_deposits ?? [], ...rest };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport { CHAIN_IDS } from \"../constants\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const TOKEN_INFO_QUERY_KEY = \"wp:token-info\";\n\n/** wallet-server API가 네이티브 자산에 쓰는 sentinel 주소 (send/types.ts와 동일 값). */\nconst NATIVE_TOKEN_SENTINEL_ADDRESS =\n  \"0x0000000000000000000000000000000000000001\";\n\ninterface TokenInfo {\n  id: number;\n  chain_id: number;\n  address: string;\n  symbol: string;\n  name: string;\n  decimals: number;\n  image: string;\n}\n\ninterface TokenInfoResponse {\n  code: number;\n  message: string;\n  data: TokenInfo[];\n}\n\nexport function useTokenInfo(env: Environment, enabled = true) {\n  const crossChainId = CHAIN_IDS[env].cross;\n\n  const { data, ...rest } = useQuery<TokenInfoResponse>({\n    queryKey: [TOKEN_INFO_QUERY_KEY, env],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"walletServer\", env);\n      const res = await fetch(`${baseUrl}/v1/public/token/info`, {\n        cache: \"no-store\",\n        signal,\n      });\n      if (!res.ok) throw new Error(`Failed to fetch token info: ${res.status}`);\n      const json: TokenInfoResponse = await res.json();\n      if (json.code !== 200) throw new Error(`Token info API error: ${json.code}`);\n      return json;\n    },\n    enabled,\n    staleTime: 5 * 60 * 1000,\n  });\n\n  const { byChainAddr, byChainSym } = useMemo(() => {\n    const addrMap = new Map<string, TokenInfo>();\n    const symMap = new Map<string, TokenInfo>();\n    if (!data?.data) return { byChainAddr: addrMap, byChainSym: symMap };\n\n    for (const t of data.data) {\n      const addr = t.address.toLowerCase();\n      addrMap.set(`${t.chain_id}:${addr}`, t);\n      addrMap.set(addr, t);\n\n      const sym = t.symbol.replace(/^t/, \"\").toUpperCase();\n      const chainSymKey = `${t.chain_id}:${sym}`;\n      if (!symMap.has(chainSymKey)) symMap.set(chainSymKey, t);\n      if (!symMap.has(sym)) symMap.set(sym, t);\n    }\n\n    return { byChainAddr: addrMap, byChainSym: symMap };\n  }, [data]);\n\n  /**\n   * wallet token info API 기준 심볼. 주소 매칭 실패 시 fallback을 그대로\n   * 반환한다 (API에 없는 토큰은 소비처가 가진 심볼 유지).\n   * `chainOnly`면 CROSS 체인(현 env의 chain_id) 엔트리만 보고, 다른 체인\n   * 엔트리로의 폴백을 막는다 (예: BSC 쪽 CROSS 오매칭 방지).\n   */\n  const getTokenSymbol = (\n    address: string,\n    fallback?: string,\n    opts?: { chainOnly?: boolean },\n  ): string | undefined => {\n    const addr = address.toLowerCase();\n    const token =\n      byChainAddr.get(`${crossChainId}:${addr}`) ??\n      (opts?.chainOnly ? undefined : byChainAddr.get(addr));\n    return token?.symbol || fallback;\n  };\n\n  /**\n   * 현 env CROSS 체인의 네이티브 토큰 엔트리. wallet-server API는 네이티브\n   * 자산을 sentinel 주소(0x…0001)로 등록하므로 그 주소로 매칭한다.\n   * WCROSS(wrapped native)의 표시 심볼/아이콘을 API 표기(stg=tONE,\n   * prod=ONE)로 해석할 때 사용. API에 엔트리 없으면 undefined.\n   */\n  const getNativeToken = (): TokenInfo | undefined =>\n    byChainAddr.get(`${crossChainId}:${NATIVE_TOKEN_SENTINEL_ADDRESS}`);\n\n  const getTokenImage = (\n    address: string,\n    symbol?: string,\n    opts?: { chainOnly?: boolean },\n  ): string => {\n    const addr = address.toLowerCase();\n    const chainOnly = !!opts?.chainOnly;\n\n    const byChain = byChainAddr.get(`${crossChainId}:${addr}`);\n    if (byChain?.image) return byChain.image;\n\n    if (!chainOnly) {\n      const byAddr = byChainAddr.get(addr);\n      if (byAddr?.image) return byAddr.image;\n    }\n\n    if (symbol) {\n      const sym = symbol.replace(/^t/, \"\").toUpperCase();\n      const byChainSymbol = byChainSym.get(`${crossChainId}:${sym}`);\n      if (byChainSymbol?.image) return byChainSymbol.image;\n\n      if (!chainOnly) {\n        const bySym = byChainSym.get(sym);\n        if (bySym?.image) return bySym.image;\n      }\n    }\n\n    return \"\";\n  };\n\n  return { getTokenImage, getTokenSymbol, getNativeToken, ...rest };\n}\n","import type { Environment } from \"../../types\";\n\nexport { WALLET_API, CHAIN_IDS } from \"../../lib/constants\";\n\nexport const REWARD_API: Record<Environment, string> = {\n  dev: \"https://stg-cross-game-reward-api.crosstoken.io/api\",\n  stage: \"https://stg-cross-game-reward-api.crosstoken.io/api\",\n  production: \"https://cross-game-reward-api.crosstoken.io/api\",\n};\n\nexport const REWARDS_WEB: Record<Environment, string> = {\n  dev: \"https://stg.onechain.nexus\",\n  stage: \"https://stg.onechain.nexus\",\n  production: \"https://onechain.nexus\",\n};\n\n/**\n * CROSS Points(My-Point) 웹 URL. Points 섹션에서 내장 Withdraw가 불가능한\n * 환경(sendTransaction 미주입)의 아웃링크 폴백 대상.\n * app-launcher-v2.json `points` 항목과 동일한 목적지.\n */\nexport const POINTS_WEB: Record<Environment, string> = {\n  dev: \"https://stg.onechain.nexus/points\",\n  stage: \"https://stg.onechain.nexus/points\",\n  production: \"https://www.onechain.nexus/points\",\n};\n\nexport const FORGE_API: Record<Environment, string> = {\n  dev: \"https://stg-bonding-curve-api.crosstoken.io/api\",\n  stage: \"https://stg-bonding-curve-api.crosstoken.io/api\",\n  production: \"https://bonding-curve-api.crosstoken.io/api\",\n};\n\nexport const DEX_WEB: Record<Environment, string> = {\n  dev: \"https://stg.onechain.nexus\",\n  stage: \"https://stg.onechain.nexus\",\n  production: \"https://onechain.nexus\",\n};\n\n/**\n * POSA(staking/governance) 백엔드 REST API. `GET /stake/:address`,\n * `GET /network/stats` 등을 제공한다. posa 레포의 env.API.GOVERNANCE 기준.\n * dev/stage는 staging 백엔드를 공유한다(다른 섹션 컨벤션과 동일).\n */\nexport const POSA_API: Record<Environment, string> = {\n  dev: \"https://stg-posa-api.crosstoken.io/api/v1\",\n  stage: \"https://stg-posa-api.crosstoken.io/api/v1\",\n  production: \"https://posa-api.crosstoken.io/api/v1\",\n};\n\n/** Staking 웹 도메인 루트. 미스테이킹 상태의 \"Stake Now\" 아웃링크 대상. */\nexport const STAKING_WEB_HOME: Record<Environment, string> = {\n  dev: \"https://dev-stake.ogfcorp.com\",\n  stage: \"https://stg-stake.ogfcorp.com\",\n  production: \"https://stake.ogfcorp.com\",\n};\n\n/** Staking 대시보드(포트폴리오) 웹 URL. 스테이킹 보유 시 \"Manage\" 아웃링크 대상. */\nexport const STAKING_WEB: Record<Environment, string> = {\n  dev: `${STAKING_WEB_HOME.dev}/portfolio`,\n  stage: `${STAKING_WEB_HOME.stage}/portfolio`,\n  production: `${STAKING_WEB_HOME.production}/portfolio`,\n};\n\n/**\n * game-swap 백엔드 REST API. `GET /addresses/:address/portfolio`로 사용자가\n * 참여한 풀 목록을 제공한다. cross-game-swap 레포의 env.API.GAME_SWAP 기준.\n * dev/stage는 staging 백엔드를 공유한다(다른 섹션 컨벤션과 동일).\n * NOTE: 도메인이 crosstoken.io가 아니라 cross.nexus 임에 유의.\n */\nexport const GAME_SWAP_API: Record<Environment, string> = {\n  dev: \"https://stg-game-swap-api.cross.nexus/v1\",\n  stage: \"https://stg-game-swap-api.cross.nexus/v1\",\n  production: \"https://game-swap-api.cross.nexus/v1\",\n};\n\n/**\n * game-swap 웹 URL. \"Manage\" 아웃링크 대상. 유동성 관리는\n * `/tokens/:tokenAddress?trade=deposit` 경로를 사용한다.\n */\nexport const GAME_SWAP_WEB: Record<Environment, string> = {\n  dev: \"https://stg.onechain.nexus\",\n  stage: \"https://stg.onechain.nexus\",\n  production: \"https://onechain.nexus\",\n};\n\nexport const APP_LAUNCHER_ICONS_BASE: Record<Environment, string> = {\n  dev: \"https://stg-contents.crosstoken.io/frontend/common/app-launcher/icons\",\n  stage: \"https://stg-contents.crosstoken.io/frontend/common/app-launcher/icons\",\n  production: \"https://contents.crosstoken.io/frontend/common/app-launcher/icons\",\n};\n\n/**\n * cross-defi(CROSSD swap) 백엔드 REST API. `GET /api/v1/positions?owner=`로\n * v3 pool 포지션 목록을 제공한다. crossd-frontend env.API.CROSS_DEFI 기준.\n * dev/stage는 staging 백엔드를 공유한다(다른 섹션 컨벤션과 동일).\n */\nexport const CROSS_DEFI_API: Record<Environment, string> = {\n  dev: \"https://stg-swap-api.crossdefi.io\",\n  stage: \"https://stg-swap-api.crossdefi.io\",\n  production: \"https://swap-api.crossdefi.io\",\n};\n\n/** CROSSD 웹 URL. sendTransaction 미주입 시 Withdraw 아웃링크 폴백 대상. */\nexport const CROSSD_WEB: Record<Environment, string> = {\n  dev: \"https://stg.crossdefi.io\",\n  stage: \"https://stg.crossdefi.io\",\n  production: \"https://www.crossdefi.io\",\n};\n\n/**\n * CROSSD v3 NonfungiblePositionManager 주소.\n * crossd-frontend assets/json/{stg,prod}/address.json 기준.\n */\nexport const CROSSD_POSITION_MANAGER: Record<Environment, `0x${string}`> = {\n  dev: \"0x1Fa8C0bc5B754E82d55D60f0d6151903FD995b1B\",\n  stage: \"0x1Fa8C0bc5B754E82d55D60f0d6151903FD995b1B\",\n  production: \"0xEFd871aF7D695A0c1709e46b565761F4cd1Ef5ba\",\n};\n\n/** WCROSS(wrapped native) 주소. crossd-frontend env.CONTRACT_ADDRESS.W_CROSS 기준. */\nexport const CROSSD_WCROSS: Record<Environment, `0x${string}`> = {\n  dev: \"0xda37a3B60E6b97E8CB44b460130d40331efc566b\",\n  stage: \"0xda37a3B60E6b97E8CB44b460130d40331efc566b\",\n  production: \"0x731a104a4461ae45bfef520468b43c262d35e682\",\n};\n","\"use client\";\n\nimport * as React from \"react\";\nimport type { PortfolioSection } from \"../../types\";\nimport { useSectionStatusReporter } from \"../../providers/section-status-provider\";\n\nexport interface SectionCardProps {\n  /** 부모에 로딩/빈 상태를 보고할 때 쓰는 섹션 식별자. */\n  sectionKey: PortfolioSection;\n  title: string;\n  icon: string;\n  iconFit?: \"cover\" | \"contain\";\n  isEmpty: boolean;\n  /** 최초 데이터 로딩 여부. 로딩 중에는 isEmpty를 확정 상태로 보지 않는다. */\n  loading?: boolean;\n  children?: React.ReactNode;\n}\n\nexport function SectionCard({\n  sectionKey,\n  title,\n  icon,\n  iconFit = \"cover\",\n  isEmpty,\n  loading = false,\n  children,\n}: SectionCardProps) {\n  const reporter = useSectionStatusReporter();\n\n  React.useEffect(() => {\n    reporter?.report(sectionKey, { loading, empty: isEmpty });\n  }, [reporter, sectionKey, loading, isEmpty]);\n\n  React.useEffect(\n    () => () => reporter?.unregister(sectionKey),\n    [reporter, sectionKey],\n  );\n\n  // 보유 자산이 없는 카테고리는 카드 자체를 그리지 않는다.\n  // 전체가 비었을 때의 안내는 부모(WalletPortfolioBody)의 전역 빈 화면이 담당.\n  if (isEmpty) return null;\n\n  return (\n    <div className=\"wp-svc-card\">\n      <div className=\"wp-svc-card-head\">\n        <img\n          className=\"wp-svc-card-icon\"\n          src={icon}\n          alt=\"\"\n          style={{ objectFit: iconFit }}\n        />\n        <span className=\"wp-svc-card-title\">{title}</span>\n      </div>\n      <div className=\"wp-svc-items\">{children}</div>\n    </div>\n  );\n}\n","\"use client\";\n\nimport {\n  useWalletPortfolioContext,\n  usePortfolioFiat,\n} from \"../../providers/wallet-portfolio-provider\";\nimport { useRewardPools } from \"../../queries/use-reward-pools\";\nimport { useUserDeposits } from \"../../queries/use-user-deposits\";\nimport { useTokenInfo } from \"../../queries/use-token-info\";\nimport { APP_LAUNCHER_ICONS_BASE, REWARDS_WEB } from \"../../constants\";\nimport type { RewardPool } from \"../../types\";\nimport { formatPortfolioTokenAmount, formatPortfolioUsd } from \"../../utils\";\nimport { SectionCard } from \"./SectionCard\";\nimport { ExternalLinkIcon } from \"../icons\";\nimport { handleOutlinkClick } from \"../../../../lib/outlink\";\nimport BigNumber from \"bignumber.js\";\n\nconst SECTION_TITLE = \"ONErewards\";\n\nfunction isPositiveRawAmount(raw: string): boolean {\n  return new BigNumber(raw || \"0\").isGreaterThan(0);\n}\n\nexport function RewardsSection() {\n  const { env, walletAddress, onOutlink } = useWalletPortfolioContext();\n  const fiat = usePortfolioFiat();\n  const hasAddress = !!walletAddress;\n  const { allPools, isLoading: poolsLoading } = useRewardPools(env, hasAddress);\n  const { depositMap, isLoading: depositsLoading } = useUserDeposits(\n    env,\n    walletAddress,\n    hasAddress,\n  );\n  const { getTokenImage } = useTokenInfo(env, hasAddress);\n\n  // 리워드 서비스가 onechain gametoken 토큰 상세(mode=rewards)로 통합돼\n  // 기존 `/rewards/<pool>` 딥링크는 pool 세그먼트가 버려진 채 리다이렉트된다.\n  // deposit 토큰 상세의 rewards 모드로 직접 보낸다 — withdraw는 withdraw 탭,\n  // claim은 기본(deposit) 탭에 Claim 버튼이 있다.\n  const getRewardsUrl = (pool: RewardPool, action: \"withdraw\" | \"claim\") =>\n    `${REWARDS_WEB[env]}/gametoken/tokens/${pool.deposit_token.address}?mode=rewards${\n      action === \"withdraw\" ? \"&trade=withdraw\" : \"\"\n    }`;\n\n  const sectionIcon = `${APP_LAUNCHER_ICONS_BASE[env]}/reward.png`;\n\n  const visiblePools = allPools.filter((pool) => {\n    const userDeposit = depositMap.get(pool.pool_address.toLowerCase());\n    const deposited = userDeposit?.deposited_amount ?? \"0\";\n    const claimable = userDeposit?.claimable_reward ?? \"0\";\n    return !(new BigNumber(deposited).isZero() && new BigNumber(claimable).isZero());\n  });\n\n  return (\n    <SectionCard\n        sectionKey=\"rewards\"\n        title={SECTION_TITLE}\n        icon={sectionIcon}\n        isEmpty={visiblePools.length === 0}\n        loading={poolsLoading || depositsLoading}\n      >\n        {visiblePools.map((pool) => {\n          const userDeposit = depositMap.get(pool.pool_address.toLowerCase());\n          const deposited = userDeposit?.deposited_amount ?? \"0\";\n          const rewardRaw = userDeposit?.claimable_reward ?? \"0\";\n          const withdrawUrl = getRewardsUrl(pool, \"withdraw\");\n          const claimUrl = getRewardsUrl(pool, \"claim\");\n          const depositIcon = getTokenImage(\n            pool.deposit_token.address,\n            pool.deposit_token.symbol,\n          );\n          const firstRewardToken = pool.reward_tokens[0];\n          const firstRewardIcon = firstRewardToken\n            ? getTokenImage(firstRewardToken.address, firstRewardToken.symbol)\n            : \"\";\n          const rewardSymbols = pool.reward_tokens.map((rt) => rt.symbol);\n          const poolLabel = rewardSymbols.length > 0\n            ? `${pool.deposit_token.symbol}-${rewardSymbols.join(\"/\")}`\n            : pool.deposit_token.symbol;\n          const rewardAmount = firstRewardToken\n            ? formatPortfolioTokenAmount(rewardRaw, firstRewardToken.decimals)\n            : \"0\";\n          const rewardSymbol = firstRewardToken?.symbol ?? \"Reward\";\n          const depositedUsd = formatPortfolioUsd(\n            deposited,\n            pool.deposit_token.decimals,\n            pool.deposit_token.price,\n            fiat,\n          );\n          const rewardUsd = firstRewardToken\n            ? formatPortfolioUsd(\n                rewardRaw,\n                firstRewardToken.decimals,\n                firstRewardToken.price,\n                fiat,\n              )\n            : null;\n          const canClaim = !!userDeposit && isPositiveRawAmount(rewardRaw);\n\n          return (\n            <article\n              className=\"wp-item\"\n              data-side=\"reward\"\n              key={pool.pool_address}\n            >\n              <div className=\"wp-item-body\">\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Pool (Deposit - Earn)</span>\n                  <div className=\"wp-svc-token\">\n                    <span className=\"wp-token-stack\" aria-hidden>\n                      {depositIcon && (\n                        <img\n                          className=\"wp-svc-token-icon\"\n                          src={depositIcon}\n                          alt=\"\"\n                        />\n                      )}\n                      {firstRewardIcon && (\n                        <img\n                          className=\"wp-svc-token-icon\"\n                          src={firstRewardIcon}\n                          alt=\"\"\n                        />\n                      )}\n                    </span>\n                    <span>{poolLabel}</span>\n                  </div>\n                </div>\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Deposited</span>\n                  <div className=\"wp-svc-token wp-svc-token--amount\">\n                    {depositIcon && (\n                      <img\n                        className=\"wp-svc-token-icon\"\n                        src={depositIcon}\n                        alt=\"\"\n                      />\n                    )}\n                    <div className=\"wp-svc-amount\">\n                      <span className=\"wp-svc-amount-main\">\n                        {formatPortfolioTokenAmount(\n                          deposited,\n                          pool.deposit_token.decimals,\n                        )}\n                      </span>\n                      {depositedUsd && (\n                        <span className=\"wp-item-usd\">{depositedUsd}</span>\n                      )}\n                    </div>\n                  </div>\n                </div>\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">\n                    Claimable ({rewardSymbol})\n                  </span>\n                  <div className=\"wp-svc-token wp-svc-token--amount\">\n                    {firstRewardIcon && (\n                      <img\n                        className=\"wp-svc-token-icon\"\n                        src={firstRewardIcon}\n                        alt=\"\"\n                      />\n                    )}\n                    <div className=\"wp-svc-amount\">\n                      <span className=\"wp-svc-amount-main\">{rewardAmount}</span>\n                      {rewardUsd && (\n                        <span className=\"wp-item-usd\">{rewardUsd}</span>\n                      )}\n                    </div>\n                  </div>\n                </div>\n              </div>\n              <div className=\"wp-item-tags\">\n                <a\n                  className=\"wp-tag wp-tag--muted\"\n                  href={withdrawUrl}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  data-track=\"rewards_withdraw_open\"\n                  onClick={(e) =>\n                    handleOutlinkClick(\n                      e,\n                      withdrawUrl,\n                      {\n                        category: \"portfolio\",\n                        origin: \"portfolio-rewards\",\n                        payload: { pool, userDeposit },\n                      },\n                      onOutlink,\n                    )\n                  }\n                >\n                  <span className=\"wp-tag-label\">Withdraw</span>\n                  <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n                </a>\n                <a\n                  className=\"wp-tag wp-tag--mint\"\n                  data-disabled={!canClaim || undefined}\n                  aria-disabled={!canClaim || undefined}\n                  href={canClaim ? claimUrl : undefined}\n                  target={canClaim ? \"_blank\" : undefined}\n                  rel={canClaim ? \"noopener noreferrer\" : undefined}\n                  onClick={(e) => {\n                    if (!canClaim) {\n                      e.preventDefault();\n                      return;\n                    }\n                    handleOutlinkClick(\n                      e,\n                      claimUrl,\n                      {\n                        category: \"portfolio\",\n                        origin: \"portfolio-rewards\",\n                        payload: { pool, userDeposit },\n                      },\n                      onOutlink,\n                    );\n                  }}\n                >\n                  <span className=\"wp-tag-label\">Claim</span>\n                  <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n                </a>\n              </div>\n            </article>\n          );\n        })}\n    </SectionCard>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n  useWalletPortfolioContext,\n  usePortfolioFiat,\n} from \"../../providers/wallet-portfolio-provider\";\nimport { useTokenInfo } from \"../../queries/use-token-info\";\nimport { useWithdrawPositions } from \"../../queries/use-withdraw-positions\";\nimport { APP_LAUNCHER_ICONS_BASE, POINTS_WEB } from \"../../constants\";\nimport {\n  formatPortfolioTokenAmount,\n  formatPortfolioUsd,\n  isPointsWithdrawPosition,\n} from \"../../utils\";\nimport { SectionCard } from \"./SectionCard\";\nimport { WithdrawDialog, type WithdrawDialogPosition } from \"./WithdrawDialog\";\nimport { useWithdrawFlow } from \"./useWithdrawFlow\";\nimport { ExternalLinkIcon } from \"../icons\";\nimport { handleOutlinkClick } from \"../../../../lib/outlink\";\n\nconst SECTION_TITLE = \"ONEpoints\";\n\n/**\n * cross-points My-Point의 예치 퀘스트(게임 토큰 예치) 포지션 섹션.\n * 게임 토큰 예치 잔액을 나열하고, Withdraw는 외부 이동 없이 내장\n * `WithdrawDialog`(토큰 리스트 + 금액 입력)로 처리한다.\n * `sendTransaction` 미주입 환경에서는 Points 웹으로 아웃링크 폴백.\n */\nexport function PointsSection() {\n  const { env, walletAddress, onOutlink } = useWalletPortfolioContext();\n  const fiat = usePortfolioFiat();\n  const hasAddress = !!walletAddress;\n  const { getTokenImage, getNativeToken } = useTokenInfo(env, hasAddress);\n  const flow = useWithdrawFlow(\"points\");\n  const { positions, isLoading: positionsLoading } = useWithdrawPositions(\n    env,\n    walletAddress,\n    hasAddress,\n  );\n\n  // ONErewards에 잡힌 활성 풀은 제외한다. pools API에 없는 quest 풀만\n  // useWithdrawPositions에서 `Unknown`으로 분류되어 ONEpoints에 노출된다.\n  const pointPositions = React.useMemo<WithdrawDialogPosition[]>(\n    () =>\n      positions\n        .filter(isPointsWithdrawPosition)\n        .map((p) => ({\n          ...p,\n          icon: getTokenImage(p.tokenAddress, p.tokenSymbol),\n        })),\n    [positions, getTokenImage],\n  );\n\n  const sectionIcon = `${APP_LAUNCHER_ICONS_BASE[env]}/point.png`;\n  const pointsUrl = POINTS_WEB[env];\n\n  return (\n    <>\n      <SectionCard\n        sectionKey=\"points\"\n        title={SECTION_TITLE}\n        icon={sectionIcon}\n        isEmpty={pointPositions.length === 0}\n        loading={positionsLoading}\n      >\n        {pointPositions.map((position) => {\n          const depositedUsd = position.price\n            ? formatPortfolioUsd(\n                position.depositedRaw,\n                position.decimals,\n                position.price,\n                fiat,\n              )\n            : null;\n\n          return (\n            <article\n              className=\"wp-item\"\n              data-side=\"reward\"\n              key={position.poolAddress}\n            >\n              <div className=\"wp-item-body\">\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Token</span>\n                  <div className=\"wp-svc-token\">\n                    {position.icon && (\n                      <img\n                        className=\"wp-svc-token-icon\"\n                        src={position.icon}\n                        alt=\"\"\n                      />\n                    )}\n                    <span>{position.tokenSymbol}</span>\n                  </div>\n                </div>\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Deposited</span>\n                  <div className=\"wp-svc-token wp-svc-token--amount\">\n                    {position.icon && (\n                      <img\n                        className=\"wp-svc-token-icon\"\n                        src={position.icon}\n                        alt=\"\"\n                      />\n                    )}\n                    <div className=\"wp-svc-amount\">\n                      <span className=\"wp-svc-amount-main\">\n                        {formatPortfolioTokenAmount(\n                          position.depositedRaw,\n                          position.decimals,\n                        )}\n                      </span>\n                      {depositedUsd && (\n                        <span className=\"wp-item-usd\">{depositedUsd}</span>\n                      )}\n                    </div>\n                  </div>\n                </div>\n              </div>\n              <div className=\"wp-item-tags\">\n                {flow.canWithdrawInline ? (\n                  <button\n                    type=\"button\"\n                    className=\"wp-tag wp-tag--muted\"\n                    data-track=\"points_withdraw_open\"\n                    onClick={() => flow.openWithdraw(position.poolAddress)}\n                  >\n                    <span className=\"wp-tag-label\">Withdraw</span>\n                  </button>\n                ) : (\n                  <a\n                    className=\"wp-tag wp-tag--muted\"\n                    href={pointsUrl}\n                    target=\"_blank\"\n                    rel=\"noopener noreferrer\"\n                    data-track=\"points_withdraw_open\"\n                    onClick={(e) =>\n                      handleOutlinkClick(\n                        e,\n                        pointsUrl,\n                        {\n                          category: \"portfolio\",\n                          origin: \"portfolio-points\",\n                          payload: { position },\n                        },\n                        onOutlink,\n                      )\n                    }\n                  >\n                    <span className=\"wp-tag-label\">Withdraw</span>\n                    <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n                  </a>\n                )}\n              </div>\n            </article>\n          );\n        })}\n      </SectionCard>\n\n      <WithdrawDialog\n        open={flow.isOpen}\n        status={flow.status}\n        positions={pointPositions}\n        initialPoolAddress={flow.initialPoolAddress}\n        walletAddress={walletAddress}\n        txHash={flow.txHash}\n        error={flow.error}\n        copied={flow.copied}\n        estimateWithdraw={flow.hasEstimator ? flow.estimateWithdraw : undefined}\n        onClose={flow.closeWithdraw}\n        onConfirm={flow.confirmWithdraw}\n        onCopyHash={flow.copyHash}\n        trackFunnel={flow.trackFunnelOnce}\n        nativeSymbol={getNativeToken()?.symbol}\n      />\n    </>\n  );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { WithdrawPosition } from \"../types\";\nimport { useRewardPools } from \"./use-reward-pools\";\nimport { useUserDeposits } from \"./use-user-deposits\";\nimport {\n  ethCall,\n  fetchCrossRpcUrl,\n  toBigIntOrUndefined,\n} from \"./lp-balance-rpc-fallback\";\n\nexport const WITHDRAW_TOKEN_META_QUERY_KEY = \"wp:withdraw-token-meta\";\n\n/** `depositToken()` — CrossGameRewardPool의 예치 토큰 주소. */\nconst DEPOSIT_TOKEN_SELECTOR = \"0xc89039c5\";\n/** `symbol()` / `decimals()` — ERC20 메타. */\nconst SYMBOL_SELECTOR = \"0x95d89b41\";\nconst DECIMALS_SELECTOR = \"0x313ce567\";\n\ninterface DepositTokenMeta {\n  tokenAddress: string;\n  symbol: string;\n  decimals: number;\n}\n\n/** ABI 인코딩된 string 반환값(offset+length+bytes) 디코드. 실패 시 undefined. */\nfunction decodeAbiString(hex: string | undefined): string | undefined {\n  if (!hex || hex === \"0x\") return undefined;\n  try {\n    const body = hex.replace(/^0x/, \"\");\n    const length = Number(BigInt(\"0x\" + body.slice(64, 128)));\n    if (!Number.isFinite(length) || length <= 0 || length > 64) return undefined;\n    const bytes = body.slice(128, 128 + length * 2);\n    let out = \"\";\n    for (let i = 0; i < bytes.length; i += 2) {\n      out += String.fromCharCode(parseInt(bytes.slice(i, i + 2), 16));\n    }\n    return out || undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n/** 32바이트 word 뒤 20바이트를 address로 디코드. */\nfunction decodeAbiAddress(hex: string | undefined): string | undefined {\n  if (!hex || hex === \"0x\") return undefined;\n  const body = hex.replace(/^0x/, \"\").padStart(64, \"0\");\n  const address = \"0x\" + body.slice(24, 64);\n  return /^0x[a-f0-9]{40}$/.test(address) &&\n    address !== \"0x0000000000000000000000000000000000000000\"\n    ? address\n    : undefined;\n}\n\n/**\n * `/v1/pools`에 없는(비활성/quest) 풀의 예치 토큰 메타를 온체인에서 읽는다.\n * pool.depositToken() → ERC20 symbol()/decimals(). LP RPC 폴백과 동일하게\n * chain-info API가 알려준 CROSS RPC로 raw eth_call 한다.\n */\nasync function readDepositTokenMeta(\n  env: Environment,\n  poolAddresses: string[],\n  signal?: AbortSignal,\n): Promise<Record<string, DepositTokenMeta>> {\n  if (poolAddresses.length === 0) return {};\n\n  let rpcUrl: string | undefined;\n  try {\n    rpcUrl = await fetchCrossRpcUrl(env, signal);\n  } catch {\n    rpcUrl = undefined;\n  }\n  if (!rpcUrl) return {};\n\n  const entries = await Promise.all(\n    poolAddresses.map(async (pool) => {\n      const tokenAddress = decodeAbiAddress(\n        await ethCall(rpcUrl as string, pool, DEPOSIT_TOKEN_SELECTOR, signal),\n      );\n      if (!tokenAddress) return [pool.toLowerCase(), null] as const;\n\n      const [symbolHex, decimalsHex] = await Promise.all([\n        ethCall(rpcUrl as string, tokenAddress, SYMBOL_SELECTOR, signal),\n        ethCall(rpcUrl as string, tokenAddress, DECIMALS_SELECTOR, signal),\n      ]);\n      const symbol = decodeAbiString(symbolHex);\n      const decimalsRaw = toBigIntOrUndefined(decimalsHex);\n      if (!symbol) return [pool.toLowerCase(), null] as const;\n\n      return [\n        pool.toLowerCase(),\n        {\n          tokenAddress,\n          symbol,\n          decimals: decimalsRaw !== undefined ? Number(decimalsRaw) : 18,\n        },\n      ] as const;\n    }),\n  );\n\n  const out: Record<string, DepositTokenMeta> = {};\n  for (const [addr, meta] of entries) {\n    if (meta) out[addr] = meta;\n  }\n  return out;\n}\n\n/**\n * 사용자가 출금할 수 있는 예치 포지션(deposited > 0) 전체.\n *\n * 데이터 소스는 공개 reward API 두 개의 합성:\n * - `/v1/users/:addr/deposits` — 사용자가 예치한 모든 풀(비활성/quest 풀 포함)\n * - `/v1/pools` — 활성 풀의 토큰 메타(symbol/decimals/price)\n *\n * deposits에는 있는데 pools에는 없는 풀(cross-points 예치 퀘스트 풀 등)은\n * 온체인 RPC로 depositToken 메타를 보강한다. 메타 확보에 실패한 풀은\n * 금액 해석(decimals)이 불가능하므로 목록에서 제외한다.\n */\nexport function useWithdrawPositions(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const hasAddress = !!walletAddress;\n  const isEnabled = enabled && hasAddress;\n  const { allPools, isLoading: poolsLoading } = useRewardPools(env, isEnabled);\n  const { deposits, isLoading: depositsLoading } = useUserDeposits(\n    env,\n    walletAddress,\n    isEnabled,\n  );\n\n  const poolMap = useMemo(() => {\n    const map = new Map<string, (typeof allPools)[number]>();\n    for (const pool of allPools) map.set(pool.pool_address.toLowerCase(), pool);\n    return map;\n  }, [allPools]);\n\n  const positiveDeposits = useMemo(\n    () =>\n      deposits.filter((d) => {\n        try {\n          return BigInt(d.deposited_amount || \"0\") > 0n;\n        } catch {\n          return false;\n        }\n      }),\n    [deposits],\n  );\n\n  // pools API가 모르는 풀만 RPC 메타 조회 대상.\n  const unknownPoolAddresses = useMemo(\n    () =>\n      positiveDeposits\n        .map((d) => d.pool_address.toLowerCase())\n        .filter((addr) => !poolMap.has(addr))\n        .sort(),\n    [positiveDeposits, poolMap],\n  );\n\n  const { data: rpcMeta, isLoading: rpcMetaLoading } = useQuery({\n    queryKey: [WITHDRAW_TOKEN_META_QUERY_KEY, env, unknownPoolAddresses],\n    queryFn: ({ signal }) =>\n      readDepositTokenMeta(env, unknownPoolAddresses, signal),\n    enabled: isEnabled && unknownPoolAddresses.length > 0,\n    // 토큰 메타(symbol/decimals)는 불변 — 포트폴리오 주기 폴링에서 제외.\n    staleTime: 24 * 60 * 60 * 1000,\n  });\n\n  const positions = useMemo<WithdrawPosition[]>(() => {\n    const out: WithdrawPosition[] = [];\n    for (const deposit of positiveDeposits) {\n      const key = deposit.pool_address.toLowerCase();\n      const pool = poolMap.get(key);\n      if (pool) {\n        out.push({\n          poolId: deposit.pool_id,\n          poolAddress: deposit.pool_address,\n          poolType: pool.pool_type,\n          tokenSymbol: pool.deposit_token.symbol,\n          tokenAddress: pool.deposit_token.address,\n          decimals: pool.deposit_token.decimals,\n          price: pool.deposit_token.price,\n          depositedRaw: deposit.deposited_amount,\n        });\n        continue;\n      }\n      const meta = rpcMeta?.[key];\n      if (meta) {\n        out.push({\n          poolId: deposit.pool_id,\n          poolAddress: deposit.pool_address,\n          poolType: \"Unknown\",\n          tokenSymbol: meta.symbol,\n          tokenAddress: meta.tokenAddress,\n          decimals: meta.decimals,\n          depositedRaw: deposit.deposited_amount,\n        });\n      }\n      // meta 미확보 풀은 decimals를 몰라 금액을 안전하게 다룰 수 없어 제외.\n    }\n    return out;\n  }, [positiveDeposits, poolMap, rpcMeta]);\n\n  // 최초 조회가 하나라도 진행 중이면 positions를 확정 상태로 보지 않는다.\n  const isLoading = poolsLoading || depositsLoading || rpcMetaLoading;\n\n  return { positions, isLoading };\n}\n","import type { Environment } from \"../../../types\";\nimport { CHAIN_IDS } from \"../constants\";\nimport { getApiBase } from \"../../../lib/remote-config\";\nimport type { LpBalanceInfo } from \"../types\";\n\n// host(wagmi 보유 측)가 `lpBalanceReader`를 주입하지 않은 dApp을 위한 내부 폴백.\n// dapp-ui는 viem/wagmi를 직접 쓰지 못하므로, send 기능의 gas-rpc 폴백과 동일하게\n// chain-info API가 알려준 RPC URL로 raw JSON-RPC `eth_call`을 보내 LP 토큰의\n// balanceOf / totalSupply / decimals를 직접 읽는다.\n//\n// 모든 wei-scale 값은 BigInt로 처리한다 (CLAUDE.md `NUMERIC_HANDLING`).\n\n/** keccak256 selector. `balanceOf(address)` */\nconst BALANCE_OF_SELECTOR = \"0x70a08231\";\n/** `totalSupply()` */\nconst TOTAL_SUPPLY_SELECTOR = \"0x18160ddd\";\n/** `decimals()` */\nconst DECIMALS_SELECTOR = \"0x313ce567\";\n\nconst RPC_TIMEOUT_MS = 8_000;\n\ninterface JsonRpcResponse<T> {\n  jsonrpc: \"2.0\";\n  id: number | string;\n  result?: T;\n  error?: { code: number; message: string };\n}\n\ninterface ChainInfoPayload {\n  rpc?: string;\n}\n\ninterface ChainInfoEnvelope {\n  data?: ChainInfoPayload | ChainInfoPayload[];\n}\n\nasync function rpcCall<T>(\n  rpcUrl: string,\n  method: string,\n  params: unknown[],\n  signal?: AbortSignal,\n): Promise<T> {\n  const controller = new AbortController();\n  const timeout = setTimeout(() => controller.abort(), RPC_TIMEOUT_MS);\n  if (signal) {\n    signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n  }\n  const res = await fetch(rpcUrl, {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/json\" },\n    body: JSON.stringify({ jsonrpc: \"2.0\", id: 1, method, params }),\n    signal: controller.signal,\n  }).finally(() => clearTimeout(timeout));\n  if (!res.ok) throw new Error(`RPC ${method} HTTP ${res.status}`);\n  const json = (await res.json()) as JsonRpcResponse<T>;\n  if (json.error) throw new Error(`RPC ${method}: ${json.error.message}`);\n  if (json.result === undefined) throw new Error(`RPC ${method}: empty result`);\n  return json.result;\n}\n\n/** 32바이트(64 hex)로 우측 정렬 패딩된 address 인자. */\nfunction encodeAddressArg(address: string): string {\n  return address.toLowerCase().replace(/^0x/, \"\").padStart(64, \"0\");\n}\n\n/** 빈 결과(`0x`)/디코드 실패는 undefined로 흡수한다. */\nexport function toBigIntOrUndefined(hex: string | undefined): bigint | undefined {\n  if (!hex || hex === \"0x\") return undefined;\n  try {\n    return BigInt(hex);\n  } catch {\n    return undefined;\n  }\n}\n\nexport async function ethCall(\n  rpcUrl: string,\n  to: string,\n  data: string,\n  signal?: AbortSignal,\n  from?: string,\n): Promise<string | undefined> {\n  try {\n    return await rpcCall<string>(\n      rpcUrl,\n      \"eth_call\",\n      [from ? { from, to, data } : { to, data }, \"latest\"],\n      signal,\n    );\n  } catch {\n    return undefined;\n  }\n}\n\n/** 단일 pair(LP)의 balanceOf/totalSupply/decimals를 병렬로 읽는다. */\nasync function readPair(\n  rpcUrl: string,\n  account: string,\n  pair: string,\n  signal?: AbortSignal,\n): Promise<LpBalanceInfo | null> {\n  const [balanceHex, supplyHex, decimalsHex] = await Promise.all([\n    ethCall(rpcUrl, pair, BALANCE_OF_SELECTOR + encodeAddressArg(account), signal),\n    ethCall(rpcUrl, pair, TOTAL_SUPPLY_SELECTOR, signal),\n    ethCall(rpcUrl, pair, DECIMALS_SELECTOR, signal),\n  ]);\n\n  const balance = toBigIntOrUndefined(balanceHex);\n  // balanceOf는 필수 — 읽지 못하면 해당 pair는 주입형 reader 미주입과 동일하게\n  // 잔고 미상으로 두고 맵에서 제외한다(섹션은 풀 정보만 표시).\n  if (balance === undefined) return null;\n\n  const decimalsRaw = toBigIntOrUndefined(decimalsHex);\n  return {\n    balance,\n    totalSupply: toBigIntOrUndefined(supplyHex),\n    decimals: decimalsRaw !== undefined ? Number(decimalsRaw) : undefined,\n  };\n}\n\n/** CROSS 체인 RPC URL을 wallet chain-info API에서 조회한다. */\nexport async function fetchCrossRpcUrl(\n  env: Environment,\n  signal?: AbortSignal,\n): Promise<string | undefined> {\n  const chainId = CHAIN_IDS[env].cross;\n  const walletApi = await getApiBase(\"walletServer\", env);\n  const res = await fetch(`${walletApi}/v1/public/chain/info/${chainId}`, {\n    cache: \"no-store\",\n    signal,\n  });\n  if (!res.ok) return undefined;\n  const json = (await res.json()) as ChainInfoEnvelope | ChainInfoPayload;\n  const payload =\n    \"data\" in json && json.data !== undefined\n      ? Array.isArray(json.data)\n        ? json.data[0]\n        : json.data\n      : (json as ChainInfoPayload);\n  return payload?.rpc || undefined;\n}\n\n/**\n * 주입형 `lpBalanceReader` 미주입 시 사용하는 내부 폴백 reader.\n * CROSS 체인 RPC로 직접 LP 잔고를 읽어 `LpBalanceReaderFn`과 동일한 형태로 반환한다.\n * RPC URL 조회/호출이 실패하면 빈 맵을 돌려 섹션이 풀 정보만 표시하도록 폴백한다.\n */\nexport async function readLpBalancesViaRpc(\n  env: Environment,\n  account: string,\n  pairAddresses: string[],\n  signal?: AbortSignal,\n): Promise<Record<string, LpBalanceInfo>> {\n  if (!account || pairAddresses.length === 0) return {};\n\n  let rpcUrl: string | undefined;\n  try {\n    rpcUrl = await fetchCrossRpcUrl(env, signal);\n  } catch {\n    rpcUrl = undefined;\n  }\n  if (!rpcUrl) return {};\n\n  const entries = await Promise.all(\n    pairAddresses.map(\n      async (pair) =>\n        [pair.toLowerCase(), await readPair(rpcUrl as string, account, pair, signal)] as const,\n    ),\n  );\n\n  const out: Record<string, LpBalanceInfo> = {};\n  for (const [addr, info] of entries) {\n    if (info) out[addr] = info;\n  }\n  return out;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { CheckIcon, ClipboardIcon, SuccessCheckIcon } from \"../icons\";\nimport type { GasEstimate, WithdrawPosition } from \"../../types\";\nimport {\n  formatGasLimit,\n  formatGweiFromWei,\n  formatNativeFeeFromWei,\n  formatPortfolioTokenAmount,\n  formatWalletAddress,\n  rewardCardFitLevel,\n  shortenTransactionHash,\n} from \"../../utils\";\n\nexport type WithdrawDialogStatus =\n  | \"confirm\"\n  | \"submitting\"\n  | \"success\"\n  | \"error\";\n\nexport type WithdrawGasEstimateStatus =\n  | \"idle\"\n  | \"loading\"\n  | \"ready\"\n  | \"error\"\n  | \"unavailable\";\n\n/** 다이얼로그 토큰 리스트 항목 = 출금 가능 포지션 + 표시용 아이콘. */\nexport interface WithdrawDialogPosition extends WithdrawPosition {\n  icon?: string;\n}\n\ninterface WithdrawDialogProps {\n  open: boolean;\n  status: WithdrawDialogStatus;\n  /** 출금 가능 포지션 목록. cross-points My-Point 모달의 토큰 리스트에 해당. */\n  positions: WithdrawDialogPosition[];\n  /** 열릴 때 선택해 둘 풀 주소. 없으면 첫 포지션. */\n  initialPoolAddress?: string;\n  walletAddress: string;\n  txHash?: `0x${string}`;\n  error?: string;\n  copied?: boolean;\n  /**\n   * 선택 포지션·입력 금액(raw wei) 기준 가스 추정. 미주입이면\n   * \"Wallet estimate\"로 표기하고 지갑 추정에 위임한다.\n   */\n  estimateWithdraw?: (\n    position: WithdrawDialogPosition,\n    amountRaw: bigint,\n  ) => Promise<GasEstimate | null>;\n  onClose: () => void;\n  onConfirm: (\n    position: WithdrawDialogPosition,\n    amountRaw: bigint,\n    estimate: GasEstimate | null,\n  ) => void;\n  onCopyHash: () => void;\n  /** withdraw 퍼널 스텝 리포터 (`useWithdrawFlow().trackFunnelOnce`). */\n  trackFunnel?: (step: string) => void;\n  /** 가스비 표시용 네이티브 토큰 심볼 (info API 기준, 예: tONE/ONE). */\n  nativeSymbol?: string;\n}\n\nconst GAS_ESTIMATE_DEBOUNCE_MS = 400;\n\nfunction formatFeeValue(\n  estimate: GasEstimate | null,\n  status: WithdrawGasEstimateStatus,\n  nativeSymbol?: string,\n) {\n  if (status === \"loading\") return \"Estimating...\";\n  if (!estimate || status === \"error\" || status === \"unavailable\") {\n    return \"Wallet estimate\";\n  }\n\n  const feePerGas = estimate.maxFeePerGas ?? estimate.gasPrice;\n  if (!feePerGas || feePerGas <= 0n || estimate.gasLimit <= 0n) {\n    return \"Wallet estimate\";\n  }\n  return `${formatNativeFeeFromWei(estimate.gasLimit * feePerGas)} ${nativeSymbol ?? \"\"}`.trim();\n}\n\nfunction formatGasMetric(\n  value: string,\n  unit: string,\n  status: WithdrawGasEstimateStatus,\n) {\n  if (status === \"loading\") return \"Estimating...\";\n  if (status === \"error\" || status === \"unavailable\") return \"-\";\n  return `${value} ${unit}`.trim();\n}\n\nfunction TokenBadge({\n  position,\n  size,\n}: {\n  position: WithdrawDialogPosition;\n  size?: number;\n}) {\n  if (position.icon) {\n    return <img src={position.icon} alt=\"\" width={size} height={size} />;\n  }\n  return <span>{position.tokenSymbol.slice(0, 1)}</span>;\n}\n\n/**\n * cross-points My-Point의 withdraw 모달을 포트폴리오 안으로 옮긴 다이얼로그.\n * 토큰 리스트에서 예치 토큰을 고르면 **예치 전량**을 출금 대상으로 고정하고,\n * 확인 문구 → 가스 추정 → 지갑 확인 → Tx Hash 표기까지 처리한다 (부분 출금\n * 없음). 상태 머신(status)은 호출 섹션이 소유하고, 이 컴포넌트는 토큰\n * 선택과 가스 추정 요청만 로컬로 관리한다.\n */\nexport function WithdrawDialog({\n  open,\n  status,\n  positions,\n  initialPoolAddress,\n  walletAddress,\n  txHash,\n  error,\n  copied = false,\n  estimateWithdraw,\n  onClose,\n  onConfirm,\n  onCopyHash,\n  trackFunnel,\n  nativeSymbol,\n}: WithdrawDialogProps) {\n  const [gasEstimate, setGasEstimate] = React.useState<GasEstimate | null>(\n    null,\n  );\n  const [gasStatus, setGasStatus] =\n    React.useState<WithdrawGasEstimateStatus>(\"idle\");\n  const gasRequestRef = React.useRef(0);\n  const debounceRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  // 마지막으로 추정한 \"풀:금액\" 키. 포트폴리오 2초 폴링으로 positions 배열이\n  // 갈리면서 selected 객체 참조가 매번 바뀌어도, 같은 입력이면 재추정하지 않는다.\n  const lastEstimateKeyRef = React.useRef<string | null>(null);\n\n  React.useEffect(() => {\n    return () => clearTimeout(debounceRef.current);\n  }, []);\n\n  // Withdraw 버튼이 풀 단위로 열리므로 대상은 initialPoolAddress로 고정된다.\n  const selected = React.useMemo(() => {\n    const key = initialPoolAddress?.toLowerCase();\n    return (\n      positions.find((p) => p.poolAddress.toLowerCase() === key) ??\n      positions[0] ??\n      null\n    );\n  }, [positions, initialPoolAddress]);\n\n  const depositedRaw = React.useMemo(() => {\n    try {\n      return BigInt(selected?.depositedRaw ?? \"0\");\n    } catch {\n      return 0n;\n    }\n  }, [selected?.depositedRaw]);\n\n  const decimals = selected?.decimals ?? 18;\n\n  React.useEffect(() => {\n    if (!open || !selected) return;\n    trackFunnel?.(\"position_selected\");\n  }, [open, selected, trackFunnel]);\n\n  const resetGas = React.useCallback(() => {\n    setGasEstimate(null);\n    setGasStatus(estimateWithdraw ? \"idle\" : \"unavailable\");\n    gasRequestRef.current += 1;\n    lastEstimateKeyRef.current = null;\n    // estimateWithdraw 콜백 정체성 변화로 상태를 리셋하지 않도록 참조만 쓴다.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  // 다이얼로그가 새로 열릴 때 가스 상태 초기화.\n  React.useEffect(() => {\n    if (!open) return;\n    resetGas();\n  }, [open, resetGas]);\n\n  // 출금 금액은 항상 예치 전량 — 선택이 잡히면 디바운스 후 가스 추정.\n  // 같은 풀·금액 조합은 한 번만 추정한다(포트폴리오 폴링이 selected 참조만\n  // 바꿔도 재추정하지 않도록).\n  React.useEffect(() => {\n    if (!open || status !== \"confirm\") {\n      clearTimeout(debounceRef.current);\n      return;\n    }\n\n    if (!estimateWithdraw || !selected) {\n      clearTimeout(debounceRef.current);\n      setGasStatus(\"unavailable\");\n      return;\n    }\n    if (depositedRaw <= 0n) {\n      clearTimeout(debounceRef.current);\n      gasRequestRef.current += 1;\n      lastEstimateKeyRef.current = null;\n      setGasEstimate(null);\n      setGasStatus(\"idle\");\n      return;\n    }\n\n    const estimateKey = `${selected.poolAddress.toLowerCase()}:${depositedRaw.toString()}`;\n    if (lastEstimateKeyRef.current === estimateKey) return;\n    lastEstimateKeyRef.current = estimateKey;\n\n    clearTimeout(debounceRef.current);\n    const requestId = gasRequestRef.current + 1;\n    gasRequestRef.current = requestId;\n    setGasStatus(\"loading\");\n\n    debounceRef.current = setTimeout(() => {\n      estimateWithdraw(selected, depositedRaw)\n        .then((estimate) => {\n          if (gasRequestRef.current !== requestId) return;\n          setGasEstimate(estimate ?? null);\n          setGasStatus(estimate ? \"ready\" : \"unavailable\");\n        })\n        .catch(() => {\n          if (gasRequestRef.current !== requestId) return;\n          setGasEstimate(null);\n          setGasStatus(\"error\");\n        });\n    }, GAS_ESTIMATE_DEBOUNCE_MS);\n  }, [open, status, depositedRaw, estimateWithdraw, selected]);\n\n  if (!open || !selected) return null;\n\n  const isSubmitting = status === \"submitting\";\n  const priorityFee = formatGweiFromWei(gasEstimate?.maxPriorityFeePerGas);\n  const maxFee = formatGweiFromWei(\n    gasEstimate?.maxFeePerGas ?? gasEstimate?.gasPrice,\n  );\n  const gasLimit = formatGasLimit(gasEstimate?.gasLimit);\n  const depositedDisplay = formatPortfolioTokenAmount(\n    selected.depositedRaw,\n    decimals,\n  );\n\n  return (\n    <div\n      className=\"wp-claim-overlay\"\n      role=\"presentation\"\n      onMouseDown={(event) => {\n        if (event.target === event.currentTarget && !isSubmitting) {\n          onClose();\n        }\n      }}\n    >\n      <section\n        className=\"wp-claim-sheet\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby=\"wp-withdraw-title\"\n        aria-label=\"Withdraw\"\n        data-status={status}\n        data-vaul-no-drag\n      >\n        <div className=\"wp-claim-grabber\" aria-hidden>\n          <span />\n        </div>\n\n        <div className=\"wp-claim-content wp-withdraw-content\">\n          {status === \"success\" && txHash ? (\n            <>\n              <span\n                className=\"wp-claim-icon wp-claim-icon--success\"\n                aria-hidden\n              >\n                <SuccessCheckIcon />\n              </span>\n\n              <div className=\"wp-claim-copy\">\n                <h3 id=\"wp-withdraw-title\" className=\"wp-claim-title\">\n                  Withdraw Submitted!\n                </h3>\n                <p className=\"wp-claim-desc\">\n                  Your withdrawal request has been submitted. Balances will\n                  update shortly after the transaction settles.\n                </p>\n              </div>\n\n              <div className=\"wp-claim-list\" aria-label=\"Withdraw transaction\">\n                <div className=\"wp-claim-list-item\">\n                  <span>Tx Hash</span>\n                  <button\n                    type=\"button\"\n                    className=\"wp-claim-hash\"\n                    onClick={onCopyHash}\n                    aria-label=\"Copy transaction hash\"\n                    data-track=\"withdraw_hash_copy\"\n                  >\n                    <strong>{shortenTransactionHash(txHash)}</strong>\n                    {copied ? <CheckIcon /> : <ClipboardIcon />}\n                  </button>\n                </div>\n              </div>\n            </>\n          ) : (\n            <>\n              {status === \"error\" && (\n                <div className=\"wp-claim-heading\">\n                  <h3 id=\"wp-withdraw-title\" className=\"wp-claim-title\">\n                    Withdraw Failed\n                  </h3>\n                  <p className=\"wp-claim-error-message\">\n                    {error || \"The withdraw request could not be completed.\"}\n                  </p>\n                </div>\n              )}\n\n              <div\n                className=\"wp-claim-reward-card\"\n                data-fit={rewardCardFitLevel(\n                  selected.tokenSymbol,\n                  depositedDisplay,\n                )}\n                aria-label=\"Deposited balance\"\n              >\n                <span className=\"wp-claim-reward-token\" aria-hidden>\n                  <TokenBadge position={selected} size={28} />\n                </span>\n                <strong className=\"wp-claim-reward-symbol\">\n                  {selected.tokenSymbol}\n                </strong>\n                <strong className=\"wp-claim-reward-amount\">\n                  {depositedDisplay}\n                </strong>\n              </div>\n\n              <div\n                className=\"wp-claim-account-card\"\n                aria-label=\"Withdraw gas fee\"\n              >\n                <div className=\"wp-claim-account-row\">\n                  <div className=\"wp-claim-account\">\n                    <span className=\"wp-claim-account-avatar\" aria-hidden />\n                    <strong>{selected.tokenSymbol} Pool</strong>\n                  </div>\n                  <span>{formatWalletAddress(walletAddress)}</span>\n                </div>\n                <span className=\"wp-claim-divider\" aria-hidden />\n                <div className=\"wp-claim-detail-list\">\n                  <div className=\"wp-claim-detail-row\">\n                    <span>Est. Tx Fee</span>\n                    <strong>\n                      {formatFeeValue(gasEstimate, gasStatus, nativeSymbol)}\n                    </strong>\n                  </div>\n                  <div className=\"wp-claim-detail-row wp-claim-detail-row--sub\">\n                    <span>Priority Fee Per Gas</span>\n                    <strong>\n                      {formatGasMetric(priorityFee, \"Gwei\", gasStatus)}\n                    </strong>\n                  </div>\n                  <div className=\"wp-claim-detail-row wp-claim-detail-row--sub\">\n                    <span>Max Fee Per Gas</span>\n                    <strong>{formatGasMetric(maxFee, \"Gwei\", gasStatus)}</strong>\n                  </div>\n                  <div className=\"wp-claim-detail-row wp-claim-detail-row--sub\">\n                    <span>Gas Limit</span>\n                    <strong>{formatGasMetric(gasLimit, \"\", gasStatus)}</strong>\n                  </div>\n                </div>\n              </div>\n            </>\n          )}\n\n          <div\n            className=\"wp-claim-actions\"\n            data-count={status === \"confirm\" || status === \"error\" ? 2 : 1}\n          >\n            {(status === \"confirm\" || status === \"error\") && (\n              <button\n                type=\"button\"\n                className=\"wp-claim-secondary\"\n                data-track=\"withdraw_cancel\"\n                onClick={onClose}\n              >\n                {status === \"error\" ? \"Close\" : \"Cancel\"}\n              </button>\n            )}\n            <button\n              type=\"button\"\n              className=\"wp-claim-primary\"\n              data-track=\"withdraw_confirm\"\n              onClick={() => {\n                if (status === \"success\") {\n                  onClose();\n                  return;\n                }\n                if (selected && depositedRaw > 0n) {\n                  onConfirm(selected, depositedRaw, gasEstimate);\n                }\n              }}\n              disabled={\n                isSubmitting ||\n                ((status === \"confirm\" || status === \"error\") &&\n                  depositedRaw <= 0n)\n              }\n            >\n              {status === \"success\" && \"Done\"}\n              {status === \"submitting\" && (\n                <span\n                  className=\"wp-claim-button-spinner\"\n                  aria-label=\"Waiting\"\n                />\n              )}\n              {status === \"error\" && \"Try Again\"}\n              {status === \"confirm\" && \"Withdraw Now\"}\n            </button>\n          </div>\n        </div>\n\n        <div className=\"wp-claim-home-indicator\" aria-hidden>\n          <span />\n        </div>\n      </section>\n    </div>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useWalletPortfolioContext } from \"../../providers/wallet-portfolio-provider\";\nimport { useRewardFactoryInfo } from \"../../queries/use-reward-factory-info\";\nimport { CHAIN_IDS } from \"../../constants\";\nimport type { GasEstimate, SendTransactionArgs } from \"../../types\";\nimport { encodeWithdrawErc20, toDisplayWalletError } from \"../../utils\";\nimport {\n  normalizeFailureReason,\n  trackDappUiFunnel,\n  type TrackDappUiFunnelOptions,\n} from \"../../../../lib/sentry/index\";\nimport type {\n  WithdrawDialogPosition,\n  WithdrawDialogStatus,\n} from \"./WithdrawDialog\";\nimport { invalidateWalletPortfolioQueries } from \"../../refresh\";\n\nfunction toHexAddress(address: string): `0x${string}` | null {\n  return /^0x[a-fA-F0-9]{40}$/.test(address)\n    ? (address as `0x${string}`)\n    : null;\n}\n\n/**\n * 내장 Withdraw 다이얼로그의 트랜잭션 상태 머신.\n * Points / Rewards 등 어느 섹션에서든 `WithdrawDialog`와 짝지어 재사용한다.\n * 다이얼로그는 토큰 선택·금액 입력만 담당하고, 제출·해시·에러·복사 상태와\n * 라우터 주소 결정(`/v1/factory/info`)은 이 훅이 소유한다.\n */\nexport function useWithdrawFlow(section: \"rewards\" | \"points\") {\n  const { env, walletAddress, sendTransaction, estimateGas } =\n    useWalletPortfolioContext();\n  const hasAddress = !!walletAddress;\n  const canWithdrawInline = !!sendTransaction;\n  const queryClient = useQueryClient();\n  const { routerAddress } = useRewardFactoryInfo(\n    env,\n    hasAddress && canWithdrawInline,\n  );\n  const rewardRouterAddress = toHexAddress(routerAddress);\n\n  const [isOpen, setIsOpen] = React.useState(false);\n  const [initialPoolAddress, setInitialPoolAddress] = React.useState<\n    string | undefined\n  >(undefined);\n  const [status, setStatus] = React.useState<WithdrawDialogStatus>(\"confirm\");\n  const [txHash, setTxHash] = React.useState<`0x${string}` | undefined>();\n  const [error, setError] = React.useState(\"\");\n  const [copied, setCopied] = React.useState(false);\n  const copyTimerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n\n  React.useEffect(() => {\n    return () => clearTimeout(copyTimerRef.current);\n  }, []);\n\n  const funnelFiredRef = React.useRef(new Set<string>());\n  const trackFunnelOnce = React.useCallback(\n    (funnelStep: string, funnelOptions?: TrackDappUiFunnelOptions) => {\n      if (funnelFiredRef.current.has(funnelStep)) return;\n      funnelFiredRef.current.add(funnelStep);\n      trackDappUiFunnel(\"withdraw\", funnelStep, {\n        ...funnelOptions,\n        tags: { section, ...funnelOptions?.tags },\n      });\n    },\n    [section],\n  );\n\n  const buildWithdrawTransaction = React.useCallback(\n    (poolId: number, amountRaw: bigint): SendTransactionArgs | null => {\n      if (!rewardRouterAddress) return null;\n      return {\n        to: rewardRouterAddress,\n        value: 0n,\n        data: encodeWithdrawErc20(poolId, amountRaw),\n        chainId: CHAIN_IDS[env].cross,\n      };\n    },\n    [env, rewardRouterAddress],\n  );\n\n  const invalidatePortfolioQueries = React.useCallback(() => {\n    invalidateWalletPortfolioQueries(queryClient);\n  }, [queryClient]);\n\n  const openWithdraw = React.useCallback(\n    (poolAddress?: string) => {\n      setInitialPoolAddress(poolAddress);\n      setIsOpen(true);\n      setStatus(\"confirm\");\n      setTxHash(undefined);\n      setError(\"\");\n      setCopied(false);\n      funnelFiredRef.current = new Set();\n      trackFunnelOnce(\"opened\");\n    },\n    [trackFunnelOnce],\n  );\n\n  const closeWithdraw = React.useCallback(() => {\n    if (status === \"submitting\") return;\n    setIsOpen(false);\n    setInitialPoolAddress(undefined);\n    setStatus(\"confirm\");\n    setTxHash(undefined);\n    setError(\"\");\n    setCopied(false);\n    if (status === \"success\") {\n      invalidatePortfolioQueries();\n    }\n  }, [status, invalidatePortfolioQueries]);\n\n  const estimateWithdraw = React.useCallback(\n    async (\n      position: WithdrawDialogPosition,\n      amountRaw: bigint,\n    ): Promise<GasEstimate | null> => {\n      if (!estimateGas) return null;\n      const tx = buildWithdrawTransaction(position.poolId, amountRaw);\n      const from = toHexAddress(walletAddress);\n      if (!tx || !from) return null;\n      return estimateGas({ ...tx, from });\n    },\n    [buildWithdrawTransaction, estimateGas, walletAddress],\n  );\n\n  const copyHash = React.useCallback(async () => {\n    if (!txHash || typeof navigator === \"undefined\" || !navigator.clipboard) {\n      return;\n    }\n\n    try {\n      await navigator.clipboard.writeText(txHash);\n      setCopied(true);\n      clearTimeout(copyTimerRef.current);\n      copyTimerRef.current = setTimeout(() => setCopied(false), 1_400);\n    } catch {\n      setCopied(false);\n    }\n  }, [txHash]);\n\n  const confirmWithdraw = React.useCallback(\n    async (\n      position: WithdrawDialogPosition,\n      amountRaw: bigint,\n      estimate: GasEstimate | null,\n    ) => {\n      if (!sendTransaction) {\n        setStatus(\"error\");\n        setError(\"Withdraw is not available for this wallet.\");\n        return;\n      }\n\n      if (!rewardRouterAddress) {\n        setStatus(\"error\");\n        setError(\"Withdraw contract address is not available.\");\n        return;\n      }\n\n      try {\n        setStatus(\"submitting\");\n        setError(\"\");\n\n        const tx = buildWithdrawTransaction(position.poolId, amountRaw);\n        if (!tx) {\n          setStatus(\"error\");\n          setError(\"Withdraw contract address is not available.\");\n          return;\n        }\n\n        trackFunnelOnce(\"submitted\");\n\n        const hash = await sendTransaction({\n          ...tx,\n          gas:\n            estimate?.gasLimit && estimate.gasLimit > 0n\n              ? estimate.gasLimit\n              : undefined,\n          maxFeePerGas:\n            estimate?.maxFeePerGas && estimate.maxFeePerGas > 0n\n              ? estimate.maxFeePerGas\n              : undefined,\n          maxPriorityFeePerGas:\n            estimate?.maxPriorityFeePerGas &&\n            estimate.maxPriorityFeePerGas > 0n\n              ? estimate.maxPriorityFeePerGas\n              : undefined,\n        });\n\n        setTxHash(hash);\n        setStatus(\"success\");\n        trackFunnelOnce(\"confirmed\");\n        invalidatePortfolioQueries();\n      } catch (err) {\n        setStatus(\"error\");\n        setError(toDisplayWalletError(err, \"Failed to request withdraw.\"));\n        trackFunnelOnce(\"confirmed\", {\n          status: \"failure\",\n          reason: normalizeFailureReason(err),\n        });\n      }\n    },\n    [\n      buildWithdrawTransaction,\n      invalidatePortfolioQueries,\n      rewardRouterAddress,\n      sendTransaction,\n      trackFunnelOnce,\n    ],\n  );\n\n  return {\n    canWithdrawInline,\n    isOpen,\n    initialPoolAddress,\n    status,\n    txHash,\n    error,\n    copied,\n    hasEstimator: !!estimateGas,\n    openWithdraw,\n    closeWithdraw,\n    estimateWithdraw,\n    confirmWithdraw,\n    copyHash,\n    trackFunnelOnce,\n  };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\nimport type { RewardFactoryInfoResponse } from \"../types\";\n\nexport const REWARD_FACTORY_INFO_QUERY_KEY = \"wp:reward-factory-info\";\n\nexport function useRewardFactoryInfo(env: Environment, enabled = true) {\n  const { data, ...rest } = useQuery<RewardFactoryInfoResponse>({\n    queryKey: [REWARD_FACTORY_INFO_QUERY_KEY, env],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"gameReward\", env);\n      const res = await fetch(`${baseUrl}/v1/factory/info`, {\n        cache: \"no-store\",\n        signal,\n      });\n      if (!res.ok) throw new Error(`Factory info fetch failed: ${res.status}`);\n      const json: RewardFactoryInfoResponse = await res.json();\n      if (json.code !== 200) {\n        throw new Error(`Factory info API error: ${json.code}`);\n      }\n      return json;\n    },\n    enabled,\n    staleTime: 0,\n  });\n\n  return {\n    factoryInfo: data?.data ?? null,\n    routerAddress: data?.data.router_address ?? \"\",\n    ...rest,\n  };\n}\n","import type { QueryClient } from \"@tanstack/react-query\";\n\n/**\n * 포트폴리오 섹션 쿼리 키가 공유하는 접두사.\n * 모든 wallet-portfolio 쿼리 키의 첫 요소는 이 접두사로 시작한다.\n */\nexport const WALLET_PORTFOLIO_QUERY_PREFIX = \"wp:\";\n\n/**\n * 포트폴리오 전체 쿼리를 무효화해 다시 불러오게 한다.\n *\n * host가 `WalletPortfolioBody`를 `showHeader={false} autoRefresh={false}`로\n * 내장하고 자체 리프레시 버튼/폴링을 소유할 때, 같은 QueryClient(peer\n * `@tanstack/react-query`)를 넘겨 호출하면 내장 헤더의 리프레시와 동일하게\n * 동작한다.\n */\nexport function invalidateWalletPortfolioQueries(\n  queryClient: QueryClient,\n): Promise<void> {\n  return queryClient.invalidateQueries({\n    predicate: (query) =>\n      typeof query.queryKey[0] === \"string\" &&\n      query.queryKey[0].startsWith(WALLET_PORTFOLIO_QUERY_PREFIX),\n  });\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n  useWalletPortfolioContext,\n  usePortfolioFiat,\n} from \"../../providers/wallet-portfolio-provider\";\nimport { useStakeInfo } from \"../../queries/use-stake-info\";\nimport { useStakeLatestReward } from \"../../queries/use-stake-rewards\";\nimport { useStakingOnchain } from \"../../queries/use-staking-onchain\";\nimport { useCrossUsdPrice } from \"../../queries/use-cross-usd-price\";\nimport { useTokenInfo } from \"../../queries/use-token-info\";\nimport { APP_LAUNCHER_ICONS_BASE, STAKING_WEB } from \"../../constants\";\nimport { formatPortfolioDecimalAmount, formatPortfolioUsd } from \"../../utils\";\nimport { SectionCard } from \"./SectionCard\";\nimport { ExternalLinkIcon } from \"../icons\";\nimport { handleOutlinkClick } from \"../../../../lib/outlink\";\nimport BigNumber from \"bignumber.js\";\n\nfunction formatApr(apr: string | undefined): string {\n  if (!apr) return \"—\";\n  const value = new BigNumber(apr);\n  if (!value.isFinite()) return \"—\";\n  return `${value.toFormat(2, BigNumber.ROUND_DOWN)}%`;\n}\n\nexport function StakeSection() {\n  const { env, walletAddress, onOutlink, stakingRewardsReader } =\n    useWalletPortfolioContext();\n  const fiat = usePortfolioFiat();\n  const hasAddress = !!walletAddress;\n  const { stakeInfo, isLoading: stakeLoading } = useStakeInfo(\n    env,\n    walletAddress,\n    hasAddress,\n  );\n  const {\n    latestReward,\n    aprAccrued,\n    isLoading: rewardLoading,\n  } = useStakeLatestReward(env, walletAddress, hasAddress);\n  // 온체인 earned()/balanceOf() — 주입형 reader가 있을 때만. 미주입 시 undefined.\n  const { onchain } = useStakingOnchain(\n    walletAddress,\n    stakingRewardsReader,\n    hasAddress,\n  );\n\n  // CROSS/USD 단가 — Staked / Rewards Earned 금액 아래 $환산가 표기에 사용.\n  const crossUsdPrice = useCrossUsdPrice(env, walletAddress, hasAddress);\n\n  // 스테이킹 자산은 네이티브 토큰 — 심볼 표기는 info API가 유일 출처\n  // (stg=tONE, prod=CROSS). 금액 단위 표기에 사용한다.\n  const { getNativeToken } = useTokenInfo(env, hasAddress);\n  const nativeSymbol = getNativeToken()?.symbol;\n  const sectionTitle = \"ONEstaking\";\n\n  const sectionIcon = `${APP_LAUNCHER_ICONS_BASE[env]}/cross-staking.png`;\n  const manageUrl = STAKING_WEB[env];\n\n  const totalStake = stakeInfo?.total_stake ?? \"0\";\n\n  // Rewards Earned: posa 포트폴리오와 동일하게 온체인 earned()(미청구 누적\n  // 리워드, raw wei)를 우선 표기한다. reader 미주입 시 API cumulative_earned\n  // (없으면 stake 요약 rewards_earned)로 폴백. 표기 직전에만 floor 포맷한다.\n  const rewardsEarned = React.useMemo(() => {\n    if (onchain) {\n      return new BigNumber(onchain.earnedWei.toString())\n        .shiftedBy(-18)\n        .toString(10);\n    }\n    return latestReward?.cumulative_earned ?? stakeInfo?.rewards_earned ?? \"0\";\n  }, [onchain, latestReward, stakeInfo]);\n\n  // 개인 실현 APR (posa 포트폴리오와 동일 공식):\n  // (차트 누적 리워드 / 원금) × (365 / 경과일) × 100. 첫날(경과일 0)은 1일로 클램프.\n  // - 분자: pickChartAccrued(aprAccrued) — 오늘(없으면 최신일) 버킷 cumulative_earned.\n  //   변동이 큰 온체인 earned() 대신 안정적인 차트 누적값을 써 posa와 동일하게 맞춘다.\n  // - 분모: 온체인 balanceOf(stakedWei) 우선, reader 미주입 시 API total_stake.\n  const principal = React.useMemo(\n    () =>\n      onchain\n        ? new BigNumber(onchain.stakedWei.toString()).shiftedBy(-18)\n        : new BigNumber(totalStake),\n    [onchain, totalStake],\n  );\n  const accrued =\n    aprAccrued !== undefined ? new BigNumber(aprAccrued) : undefined;\n  const daysSinceFirstStake = stakeInfo?.days_since_first_stake;\n  const myApr =\n    accrued !== undefined &&\n    accrued.isFinite() &&\n    principal.isGreaterThan(0) &&\n    daysSinceFirstStake !== null &&\n    daysSinceFirstStake !== undefined\n      ? accrued\n          .div(principal)\n          .times(365)\n          .div(Math.max(1, daysSinceFirstStake))\n          .times(100)\n      : undefined;\n\n  // 스테이킹 잔액과 누적 리워드가 모두 0이면 미스테이킹 상태 —\n  // 보유 자산이 없는 카테고리이므로 섹션 카드 자체를 숨긴다.\n  const hasStake = !(\n    new BigNumber(totalStake).isZero() && new BigNumber(rewardsEarned).isZero()\n  );\n\n  // totalStake / rewardsEarned는 이미 CROSS 십진 문자열이므로 decimals=0.\n  // 단가를 못 구하면(null) 환산가 줄을 숨긴다.\n  const stakedUsd = formatPortfolioUsd(totalStake, 0, crossUsdPrice, fiat);\n  const rewardsEarnedUsd = formatPortfolioUsd(\n    rewardsEarned,\n    0,\n    crossUsdPrice,\n    fiat,\n  );\n\n  return (\n    <SectionCard\n      sectionKey=\"staking\"\n      title={sectionTitle}\n      icon={sectionIcon}\n      iconFit=\"contain\"\n      isEmpty={!hasStake}\n      loading={stakeLoading || rewardLoading}\n    >\n      <article className=\"wp-item\" data-side=\"stake\">\n        <div className=\"wp-item-body\">\n          <div className=\"wp-item-line\">\n            <span className=\"wp-item-label\">Staked</span>\n            <div className=\"wp-svc-amount\">\n              <span className=\"wp-svc-amount-main\">\n                {`${formatPortfolioDecimalAmount(totalStake)} ${nativeSymbol ?? \"\"}`.trim()}\n              </span>\n              {stakedUsd && <span className=\"wp-item-usd\">{stakedUsd}</span>}\n            </div>\n          </div>\n          <div className=\"wp-item-line\">\n            <span className=\"wp-item-label\">Rewards Earned</span>\n            <div className=\"wp-svc-amount\">\n              <span className=\"wp-svc-amount-main\">\n                {`${formatPortfolioDecimalAmount(rewardsEarned)} ${nativeSymbol ?? \"\"}`.trim()}\n              </span>\n              {rewardsEarnedUsd && (\n                <span className=\"wp-item-usd\">{rewardsEarnedUsd}</span>\n              )}\n            </div>\n          </div>\n          <div className=\"wp-item-line\">\n            <span className=\"wp-item-label\">My APR</span>\n            <span>{formatApr(myApr?.toString(10))}</span>\n          </div>\n        </div>\n        <div className=\"wp-item-tags\">\n          <a\n            className=\"wp-tag wp-tag--mint\"\n            href={manageUrl}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            data-track=\"stake_outlink\"\n            onClick={(e) =>\n              handleOutlinkClick(\n                e,\n                manageUrl,\n                {\n                  category: \"portfolio\",\n                  origin: \"portfolio-stake\",\n                  payload: { stakeInfo },\n                },\n                onOutlink,\n              )\n            }\n          >\n            <span className=\"wp-tag-label\">Manage</span>\n            <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n          </a>\n        </div>\n      </article>\n    </SectionCard>\n  );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { StakeInfo, StakeInfoResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const STAKE_INFO_QUERY_KEY = \"wp:stake-info\";\n\n/** POSA `GET /stake/:address` — 지갑의 스테이킹 요약(총량/리워드/위임수)을 조회. */\nexport function useStakeInfo(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const isEnabled = enabled && !!walletAddress;\n\n  const { data, ...rest } = useQuery<StakeInfoResponse>({\n    queryKey: [STAKE_INFO_QUERY_KEY, env, walletAddress],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"posa\", env);\n      const res = await fetch(`${baseUrl}/stake/${walletAddress}`, {\n        cache: \"no-store\",\n        signal,\n      });\n      if (!res.ok) throw new Error(`Stake info fetch failed: ${res.status}`);\n      const json: StakeInfoResponse = await res.json();\n      // POSA envelope: 성공 코드는 0 또는 200.\n      if (json.code !== 0 && json.code !== 200) {\n        throw new Error(`Stake info API error: ${json.code}`);\n      }\n      return json;\n    },\n    enabled: isEnabled,\n    staleTime: 0,\n  });\n\n  const stakeInfo: StakeInfo | undefined = data?.data;\n\n  return { stakeInfo, ...rest };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { StakeRewardRecord, StakeRewardsResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\nimport { pickAccruedCumulativeEarned } from \"../utils\";\n\nexport const STAKE_REWARDS_QUERY_KEY = \"wp:stake-rewards\";\n\n/** 개인 실현 APR 분자 계산에 충분한 일자 버킷 확보용 조회 한도(약 1개월). */\nconst REWARDS_HISTORY_LIMIT = 31;\n\n/**\n * POSA `GET /rewards/:address?limit=31` — 최근 리워드 스냅샷을 조회한다.\n * 응답이 최신순 정렬이라 `latestReward`(첫 레코드)가 최신이며, `aprAccrued`는\n * posa 포트폴리오 `pickChartAccrued`와 동일하게 일자 버킷팅 후 오늘(없으면\n * 최신일) 버킷의 `cumulative_earned`를 고른 값으로, 개인 실현 APR 분자로 쓴다.\n * (`/stake/:address`의 `rewards_earned`는 staging에서 항상 0이라 쓰지 않는다.)\n */\nexport function useStakeLatestReward(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const isEnabled = enabled && !!walletAddress;\n\n  const { data, ...rest } = useQuery<StakeRewardRecord[]>({\n    queryKey: [STAKE_REWARDS_QUERY_KEY, env, walletAddress],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"posa\", env);\n      const res = await fetch(\n        `${baseUrl}/rewards/${walletAddress}?limit=${REWARDS_HISTORY_LIMIT}`,\n        { cache: \"no-store\", signal },\n      );\n      if (!res.ok) throw new Error(`Stake rewards fetch failed: ${res.status}`);\n      const json: StakeRewardsResponse = await res.json();\n      // POSA envelope: 성공 코드는 0 또는 200.\n      if (json.code !== 0 && json.code !== 200) {\n        throw new Error(`Stake rewards API error: ${json.code}`);\n      }\n      return json.data?.data ?? [];\n    },\n    enabled: isEnabled,\n    staleTime: 0,\n  });\n\n  return {\n    latestReward: data?.[0] ?? null,\n    aprAccrued: pickAccruedCumulativeEarned(data),\n    ...rest,\n  };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { StakingRewardsInfo, StakingRewardsReaderFn } from \"../types\";\n\nexport const STAKING_ONCHAIN_QUERY_KEY = \"wp:staking-onchain\";\n\n/**\n * 주입형 `stakingRewardsReader`로 연결 계정의 온체인 staking 값을 조회한다.\n * POSA delegation pool의 `earned()`(누적 미청구 리워드)와 `balanceOf()`(원금)를\n * raw wei BigInt로 돌려받아, posa 포트폴리오 \"Accrued Rewards\"와 동일한 수치를\n * 표기/계산하는 데 쓴다. reader 미주입 시 비활성화되고 undefined를 반환한다.\n *\n * queryKey에 walletAddress를 포함해 계정 전환 시 자동 갱신되게 한다.\n * (reader는 함수라 직렬화되지 않으므로 key에 넣지 않는다.)\n * `wp:` prefix 덕분에 포트폴리오 주기 새로고침에 함께 갱신된다.\n */\nexport function useStakingOnchain(\n  walletAddress: string,\n  reader: StakingRewardsReaderFn | undefined,\n  enabled = true,\n) {\n  const isEnabled = enabled && !!reader && !!walletAddress;\n\n  const { data, ...rest } = useQuery<StakingRewardsInfo>({\n    queryKey: [STAKING_ONCHAIN_QUERY_KEY, walletAddress],\n    queryFn: async () => {\n      if (!reader) throw new Error(\"stakingRewardsReader not provided\");\n      return reader(walletAddress);\n    },\n    enabled: isEnabled,\n    staleTime: 0,\n  });\n\n  return { onchain: data, ...rest };\n}\n","import { useMemo } from \"react\";\nimport BigNumber from \"bignumber.js\";\nimport type { Environment } from \"../../../types\";\nimport { CHAIN_IDS } from \"../constants\";\nimport { useRewardPools } from \"./use-reward-pools\";\nimport { useTokenBalance } from \"../../wallet-info/queries/use-token-balance\";\nimport {\n  useTokenStats,\n  buildStatsKey,\n} from \"../../wallet-info/queries/use-token-stats\";\n\nfunction isCrossSymbol(symbol: string | undefined): boolean {\n  return symbol?.trim().toUpperCase() === \"ONE\";\n}\n\nfunction isPositivePrice(price: string | undefined): boolean {\n  return new BigNumber(price || \"0\").isGreaterThan(0);\n}\n\n/**\n * CROSS/USD 단가(문자열)를 해석한다. 못 구하면 \"0\".\n *\n * Stake/Forge 섹션은 CROSS 단위 금액을 USD로 환산해야 하는데, token-stats 응답에는\n * symbol이 없어 CROSS 항목을 주소 없이 직접 찾을 수 없다. 그래서 두 소스를 둔다:\n *\n *  1순위) reward 풀 토큰(deposit/reward)에 USD price와 함께 내려오는 CROSS.\n *         pool config라서 사용자 보유와 무관하게 존재 → CROSS 잔고가 0인(스테이킹만\n *         한) 유저도 커버된다. 이미 fetch 중인 쿼리라 추가 비용이 없다.\n *  2순위) 1순위 실패 시에만, 잔고의 cross 체인 \"CROSS\" 토큰 주소로 token-stats\n *         단가를 조회한다. (이 fallback일 때만 잔고/스탯 쿼리를 활성화한다.)\n */\nexport function useCrossUsdPrice(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n): string {\n  const { allPools } = useRewardPools(env, enabled);\n\n  const rewardCrossPrice = useMemo(() => {\n    for (const pool of allPools) {\n      for (const token of [pool.deposit_token, ...pool.reward_tokens]) {\n        if (isCrossSymbol(token?.symbol) && isPositivePrice(token.price)) {\n          return token.price;\n        }\n      }\n    }\n    return undefined;\n  }, [allPools]);\n\n  // 1순위가 없을 때만 잔고/스탯 쿼리를 켜서 불필요한 폴링을 피한다.\n  const needFallback = enabled && rewardCrossPrice === undefined;\n  const { tokens } = useTokenBalance(env, walletAddress, needFallback);\n  const { statsMap } = useTokenStats(env, needFallback);\n  const crossChainId = CHAIN_IDS[env].cross;\n\n  return useMemo(() => {\n    if (rewardCrossPrice) return rewardCrossPrice;\n    const crossToken = tokens.find(\n      (t) => t.chainId === crossChainId && isCrossSymbol(t.symbol),\n    );\n    if (crossToken) {\n      const stat = statsMap.get(\n        buildStatsKey(crossToken.chainId, crossToken.address),\n      );\n      if (stat && isPositivePrice(stat.price)) return stat.price;\n    }\n    return \"0\";\n  }, [rewardCrossPrice, tokens, statsMap, crossChainId]);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n  useWalletPortfolioContext,\n  usePortfolioFiat,\n} from \"../../providers/wallet-portfolio-provider\";\nimport { useGameSwapPools } from \"../../queries/use-game-swap-pools\";\nimport { useGameSwapLpBalances } from \"../../queries/use-game-swap-lp-balances\";\nimport { useTokenInfo } from \"../../queries/use-token-info\";\nimport { APP_LAUNCHER_ICONS_BASE, GAME_SWAP_WEB } from \"../../constants\";\nimport type { GameSwapPool, LpBalanceInfo } from \"../../types\";\nimport {\n  formatFiatFromUsd,\n  formatPortfolioTokenAmount,\n  type PortfolioFiat,\n} from \"../../utils\";\nimport { SectionCard } from \"./SectionCard\";\nimport { ExternalLinkIcon } from \"../icons\";\nimport { handleOutlinkClick } from \"../../../../lib/outlink\";\nimport BigNumber from \"bignumber.js\";\n\nconst SECTION_TITLE = \"Gametoken LP\";\nconst DEFAULT_DECIMALS = 18;\n/** game-swap FE와 동일하게 LP 0.0001 미만 포지션은 dust로 숨긴다. */\nconst LP_DUST_THRESHOLD = new BigNumber(\"0.0001\");\n\nfunction isCross(symbol: string | undefined): boolean {\n  return symbol?.trim().toUpperCase() === \"ONE\";\n}\n\n/**\n * 백엔드가 native(ONE) 쪽 token ref를 빈 값(symbol \"\", decimals 0)으로 내려주므로,\n * 0/미지정 decimals는 unset으로 보고 18(ONE native)로 간주한다.\n */\nfunction tokenDecimals(decimals: number | undefined): number {\n  return decimals && decimals > 0 ? decimals : DEFAULT_DECIMALS;\n}\n\n/** 풀의 CROSS 쪽 예비량을 기준으로 한 총 유동성 가치(CROSS) = 2 × crossReserve. */\nfunction poolCrossValue(pool: GameSwapPool): BigNumber {\n  const crossOnA = isCross(pool.token_a?.symbol) && !isCross(pool.token_b?.symbol);\n  const reserve = crossOnA ? pool.reserve_a : pool.reserve_b;\n  const decimals = tokenDecimals(\n    crossOnA ? pool.token_a?.decimals : pool.token_b?.decimals,\n  );\n  const crossAmount = new BigNumber(reserve || \"0\").shiftedBy(-decimals);\n  if (!crossAmount.isFinite() || crossAmount.isLessThanOrEqualTo(0)) {\n    return new BigNumber(0);\n  }\n  return crossAmount.times(2);\n}\n\ninterface PoolRow {\n  pool: GameSwapPool;\n  tokenASymbol: string;\n  tokenBSymbol: string;\n  info?: LpBalanceInfo;\n  lpAmountRaw?: string;\n  lpDecimals: number;\n  sharePercent?: BigNumber;\n  valueText?: string;\n}\n\nfunction buildRow(\n  pool: GameSwapPool,\n  info: LpBalanceInfo | undefined,\n  fiat: PortfolioFiat,\n  nativeSymbol: string | undefined,\n): PoolRow {\n  const tokenASymbol = pool.token_a?.symbol?.trim() || \"TOKEN\";\n  // 백엔드가 CROSS 쪽 token ref를 빈 값으로 내려주는 풀은 네이티브 토큰\n  // 페어 — 심볼은 info API 네이티브 엔트리(stg=tONE, prod=CROSS)가 출처.\n  const tokenBSymbol = pool.token_b?.symbol?.trim() || nativeSymbol || \"?\";\n  const lpDecimals = info?.decimals ?? DEFAULT_DECIMALS;\n\n  let sharePercent: BigNumber | undefined;\n  let valueText: string | undefined;\n\n  if (info && info.totalSupply && info.totalSupply > BigInt(0)) {\n    const balance = new BigNumber(info.balance.toString());\n    const totalSupply = new BigNumber(info.totalSupply.toString());\n    const ratio = balance.div(totalSupply);\n    sharePercent = ratio.times(100);\n\n    const positionCross = ratio.times(poolCrossValue(pool));\n    if (positionCross.isGreaterThan(0)) {\n      const crossUsd = new BigNumber(pool.quote_usd ?? pool.cross_usd ?? \"0\");\n      valueText = crossUsd.isGreaterThan(0)\n        ? formatFiatFromUsd(positionCross.times(crossUsd), fiat)\n        : `${positionCross.toFormat(4, BigNumber.ROUND_DOWN)} ${nativeSymbol ?? \"\"}`.trim();\n    }\n  }\n\n  return {\n    pool,\n    tokenASymbol,\n    tokenBSymbol,\n    info,\n    lpAmountRaw: info?.balance.toString(),\n    lpDecimals,\n    sharePercent,\n    valueText,\n  };\n}\n\nexport function GameSwapSection() {\n  const { env, walletAddress, onOutlink, lpBalanceReader } =\n    useWalletPortfolioContext();\n  const fiat = usePortfolioFiat();\n  const hasAddress = !!walletAddress;\n  const { pools, isLoading: poolsLoading } = useGameSwapPools(\n    env,\n    walletAddress,\n    hasAddress,\n  );\n\n  const pairAddresses = React.useMemo(\n    () => pools.map((p) => p.pair_address).filter(Boolean),\n    [pools],\n  );\n  const { balances, isLoading: balancesLoading } = useGameSwapLpBalances(\n    env,\n    walletAddress,\n    pairAddresses,\n    lpBalanceReader,\n  );\n\n  // 네이티브 토큰 심볼(페어의 CROSS 쪽 표기)은 info API가 유일 출처\n  // (stg=tONE, prod=CROSS).\n  const { getNativeToken } = useTokenInfo(env, hasAddress);\n  const nativeSymbol = getNativeToken()?.symbol;\n\n  // 전용 game-swap 아이콘이 버킷에 확인되지 않아, 존재가 검증된 dex.png를 사용한다.\n  // 전용 에셋이 추가되면 파일명만 교체하면 된다.\n  const sectionIcon = `${APP_LAUNCHER_ICONS_BASE[env]}/game-token.png`;\n  // 주입형 reader 또는 내부 RPC 폴백으로 잔고가 실제 조회됐는지 여부.\n  // 둘 다 실패하면 빈 맵이라 dust 필터링 없이 풀 정보만 표시한다.\n  const hasBalances = Object.keys(balances).length > 0;\n\n  const rows = React.useMemo(() => {\n    const built = pools.map((pool) =>\n      buildRow(\n        pool,\n        balances[pool.pair_address?.toLowerCase()],\n        fiat,\n        nativeSymbol,\n      ),\n    );\n    // 온체인 잔고를 알 때만 잔고 기준 dust 필터링이 가능하다.\n    if (!hasBalances) return built;\n    return built.filter((row) => {\n      if (!row.lpAmountRaw) return false;\n      const amount = new BigNumber(row.lpAmountRaw).shiftedBy(-row.lpDecimals);\n      return amount.isGreaterThanOrEqualTo(LP_DUST_THRESHOLD);\n    });\n  }, [pools, balances, hasBalances, fiat, nativeSymbol]);\n\n  const getManageUrl = (pool: GameSwapPool) => {\n    const tokenAddress = pool.token_a?.address ?? pool.pair_address;\n    return `${GAME_SWAP_WEB[env]}/gametoken/tokens/${tokenAddress}?trade=deposit`;\n  };\n\n  return (\n    <SectionCard\n      sectionKey=\"gameSwap\"\n      title={SECTION_TITLE}\n      icon={sectionIcon}\n      iconFit=\"contain\"\n      isEmpty={rows.length === 0}\n      loading={poolsLoading || balancesLoading}\n    >\n      {rows.map((row) => {\n        const { pool } = row;\n        const manageUrl = getManageUrl(pool);\n        const pairLabel = `${row.tokenASymbol} / ${row.tokenBSymbol}`;\n        const tokenAIcon = pool.token_a?.logo_url;\n        const tokenBIcon = pool.token_b?.logo_url;\n\n        return (\n          <article className=\"wp-item\" data-side=\"game-swap\" key={pool.pair_address}>\n            <div className=\"wp-item-body\">\n              <div className=\"wp-item-line\">\n                <span className=\"wp-item-label\">Pool</span>\n                <div className=\"wp-svc-token\">\n                  <span className=\"wp-token-stack\" aria-hidden>\n                    {tokenAIcon && (\n                      <img className=\"wp-svc-token-icon\" src={tokenAIcon} alt=\"\" />\n                    )}\n                    {tokenBIcon && (\n                      <img className=\"wp-svc-token-icon\" src={tokenBIcon} alt=\"\" />\n                    )}\n                  </span>\n                  <span>{pairLabel}</span>\n                </div>\n              </div>\n              {row.lpAmountRaw !== undefined && (\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">My LP</span>\n                  <span>\n                    {formatPortfolioTokenAmount(row.lpAmountRaw, row.lpDecimals)} LP\n                  </span>\n                </div>\n              )}\n              {row.sharePercent !== undefined && (\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Pool Share</span>\n                  <span>\n                    {row.sharePercent.toFormat(2, BigNumber.ROUND_DOWN)}%\n                  </span>\n                </div>\n              )}\n              {row.valueText !== undefined && (\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Value</span>\n                  <span>{row.valueText}</span>\n                </div>\n              )}\n            </div>\n            <div className=\"wp-item-tags\">\n              <a\n                className=\"wp-tag wp-tag--mint\"\n                href={manageUrl}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                data-track=\"gameswap_outlink\"\n                data-track-id={pairLabel}\n                onClick={(e) =>\n                  handleOutlinkClick(\n                    e,\n                    manageUrl,\n                    {\n                      category: \"portfolio\",\n                      origin: \"portfolio-game-swap\",\n                      payload: { pool },\n                    },\n                    onOutlink,\n                  )\n                }\n              >\n                <span className=\"wp-tag-label\">Manage</span>\n                <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n              </a>\n            </div>\n          </article>\n        );\n      })}\n    </SectionCard>\n  );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { GameSwapPool, GameSwapPortfolioResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const GAME_SWAP_POOLS_QUERY_KEY = \"wp:game-swap-pools\";\n\n/**\n * game-swap `GET /addresses/:address/portfolio` — 사용자가 참여한 LP 풀 목록.\n * 응답 body는 envelope 없이 `{ holdings, pools }`가 최상위로 내려온다.\n */\nexport function useGameSwapPools(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const isEnabled = enabled && !!walletAddress;\n\n  const { data, ...rest } = useQuery<GameSwapPool[]>({\n    queryKey: [GAME_SWAP_POOLS_QUERY_KEY, env, walletAddress],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"gameSwap\", env);\n      const res = await fetch(\n        `${baseUrl}/addresses/${walletAddress}/portfolio`,\n        { cache: \"no-store\", signal },\n      );\n      if (!res.ok) throw new Error(`Game-swap portfolio fetch failed: ${res.status}`);\n      const json: GameSwapPortfolioResponse = await res.json();\n      return json.pools ?? [];\n    },\n    enabled: isEnabled,\n    staleTime: 0,\n  });\n\n  return { pools: data ?? [], ...rest };\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { LpBalanceInfo, LpBalanceReaderFn } from \"../types\";\nimport { readLpBalancesViaRpc } from \"./lp-balance-rpc-fallback\";\n\nexport const GAME_SWAP_LP_BALANCES_QUERY_KEY = \"wp:game-swap-lp-balances\";\n\n/**\n * pair(LP) 주소들의 온체인 잔고/총발행량을 조회한다.\n * 주입형 `lpBalanceReader`가 있으면 그것을 쓰고, 없으면 CROSS 체인 RPC로\n * 직접 읽는 내부 폴백(`readLpBalancesViaRpc`)을 사용한다. 폴백마저 실패하면\n * 빈 맵을 돌려 섹션이 풀 정보만 표시하도록 한다.\n *\n * queryKey에 walletAddress를 포함해 계정 전환 시 자동 갱신되게 한다.\n * (reader는 함수라 직렬화되지 않으므로 주입 여부 boolean만 key에 넣는다.)\n */\nexport function useGameSwapLpBalances(\n  env: Environment,\n  walletAddress: string,\n  pairAddresses: string[],\n  reader: LpBalanceReaderFn | undefined,\n) {\n  const sortedPairs = [...pairAddresses]\n    .map((a) => a.toLowerCase())\n    .sort();\n  const isEnabled = !!walletAddress && sortedPairs.length > 0;\n\n  const { data, ...rest } = useQuery<Record<string, LpBalanceInfo>>({\n    queryKey: [\n      GAME_SWAP_LP_BALANCES_QUERY_KEY,\n      env,\n      walletAddress,\n      sortedPairs,\n      !!reader,\n    ],\n    queryFn: async ({ signal }) => {\n      const result = reader\n        ? await reader(sortedPairs)\n        : await readLpBalancesViaRpc(env, walletAddress, sortedPairs, signal);\n      // key를 소문자로 정규화해 조회 일관성 보장.\n      const normalized: Record<string, LpBalanceInfo> = {};\n      for (const [addr, info] of Object.entries(result)) {\n        normalized[addr.toLowerCase()] = info;\n      }\n      return normalized;\n    },\n    enabled: isEnabled,\n    staleTime: 0,\n  });\n\n  return { balances: data ?? {}, ...rest };\n}\n","\"use client\";\n\nimport BigNumber from \"bignumber.js\";\nimport { useMemo } from \"react\";\nimport {\n  useWalletPortfolioContext,\n  usePortfolioFiat,\n} from \"../../providers/wallet-portfolio-provider\";\nimport { useForgePools } from \"../../queries/use-forge-pools\";\nimport { useForgeTokenDetails } from \"../../queries/use-forge-token-details\";\nimport { useCrossUsdPrice } from \"../../queries/use-cross-usd-price\";\nimport { APP_LAUNCHER_ICONS_BASE, DEX_WEB } from \"../../constants\";\nimport { formatFiatFromUsd, type PortfolioFiat } from \"../../utils\";\nimport type { ForgePool, ForgeTokenDetail } from \"../../types\";\nimport { SectionCard } from \"./SectionCard\";\nimport { ExternalLinkIcon } from \"../icons\";\nimport { handleOutlinkClick } from \"../../../../lib/outlink\";\n\nconst SECTION_TITLE = \"ONEforge\";\n\nconst LP_DECIMALS = 18;\n\nconst FALLBACK_ICON =\n  \"https://contents.crosstoken.io/wallet/token/images/FT.png\";\n\nfunction formatLp(raw: string): string {\n  if (!raw || raw === \"0\") return \"0\";\n  const bn = new BigNumber(raw).shiftedBy(-LP_DECIMALS);\n  if (bn.isZero()) return \"0\";\n  return bn.isGreaterThanOrEqualTo(1)\n    ? bn.toFormat(2, BigNumber.ROUND_DOWN)\n    : bn.toFormat(4, BigNumber.ROUND_DOWN);\n}\n\nfunction formatOwnership(raw: string): string {\n  const n = Number(raw);\n  if (!Number.isFinite(n) || n <= 0) return \"0%\";\n  if (n >= 1) return `${(Math.floor(n * 100) / 100).toFixed(2)}%`;\n  return `${(Math.floor(n * 10000) / 10000).toFixed(4)}%`;\n}\n\n/**\n * 사용자 Forge LP 포지션의 USD 환산가. game-swap 풀가치 산식과 동일하게,\n * 풀 총 유동성(CROSS) = 2 × reserve_b(CROSS 쪽 예비량)로 보고:\n *\n *   positionCross = (lp_balance / total_supply) × (2 × reserve_b)\n *   usd           = positionCross × crossUsdPrice\n *\n * reserve_b는 CROSS(네이티브) 쪽이며 18 decimals. detail/예비량/total_supply/\n * 단가 중 하나라도 없거나 0이면 null → 환산가 줄을 숨긴다.\n */\nfunction computeForgeLpUsd(\n  pool: ForgePool,\n  detail: ForgeTokenDetail | undefined,\n  crossUsdPrice: string,\n  fiat: PortfolioFiat,\n): string | null {\n  const price = new BigNumber(crossUsdPrice || \"0\");\n  if (!price.isGreaterThan(0)) return null;\n\n  const totalSupply = new BigNumber(pool.total_supply || \"0\");\n  const lpBalance = new BigNumber(pool.lp_balance || \"0\");\n  const crossReserve = new BigNumber(detail?.reserve_b || \"0\").shiftedBy(\n    -LP_DECIMALS,\n  );\n  if (\n    !totalSupply.isGreaterThan(0) ||\n    !lpBalance.isGreaterThan(0) ||\n    !crossReserve.isGreaterThan(0)\n  ) {\n    return null;\n  }\n\n  const poolCrossValue = crossReserve.times(2);\n  const positionCross = lpBalance.div(totalSupply).times(poolCrossValue);\n  const usd = positionCross.times(price);\n  if (!usd.isFinite()) return null;\n  return formatFiatFromUsd(usd, fiat);\n}\n\nexport function ForgeSection() {\n  const { env, walletAddress, onOutlink } = useWalletPortfolioContext();\n  const fiat = usePortfolioFiat();\n  const hasAddress = !!walletAddress;\n  const { pools, isEmpty, isLoading } = useForgePools(\n    env,\n    walletAddress,\n    hasAddress,\n  );\n\n  const tokenAddresses = useMemo(\n    () => pools.map((p) => p.token.address),\n    [pools],\n  );\n  const { detailMap } = useForgeTokenDetails(\n    env,\n    tokenAddresses,\n    hasAddress && tokenAddresses.length > 0,\n  );\n\n  // CROSS/USD 단가 — LP Balance 아래 $환산가 표기에 사용.\n  const crossUsdPrice = useCrossUsdPrice(env, walletAddress, hasAddress);\n\n  const getForgeTokenUrl = (pool: ForgePool) =>\n    `${DEX_WEB[env]}/forge/token/${pool.token.address}`;\n\n  const sectionIcon = `${APP_LAUNCHER_ICONS_BASE[env]}/forge.png`;\n\n  return (\n    <SectionCard\n      sectionKey=\"forge\"\n      title={SECTION_TITLE}\n      icon={sectionIcon}\n      iconFit=\"contain\"\n      isEmpty={isEmpty}\n      loading={isLoading}\n    >\n      {pools.map((pool) => {\n        const detail = detailMap.get(pool.token.address.toLowerCase());\n        const img =\n          detail?.image_url || detail?.image || pool.token.image || FALLBACK_ICON;\n        const symbol = detail?.symbol || pool.token.symbol;\n        const forgeTokenUrl = getForgeTokenUrl(pool);\n        const lpUsd = computeForgeLpUsd(pool, detail, crossUsdPrice, fiat);\n\n        return (\n          <a\n            className=\"wp-item\"\n            data-side=\"lp\"\n            key={pool.pair_address}\n            href={forgeTokenUrl}\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            data-track=\"forge_outlink\"\n            data-track-id={symbol}\n            onClick={(e) =>\n              handleOutlinkClick(\n                e,\n                forgeTokenUrl,\n                {\n                  category: \"portfolio\",\n                  origin: \"portfolio-forge\",\n                  payload: { pool, tokenDetail: detail },\n                },\n                onOutlink,\n              )\n            }\n          >\n            <div className=\"wp-item-body\">\n              <div className=\"wp-item-line\">\n                <span className=\"wp-item-label\">Asset</span>\n                <div className=\"wp-svc-token\">\n                  <img className=\"wp-svc-token-icon\" src={img} alt=\"\" />\n                  <span>{symbol}</span>\n                  {detail?.name && (\n                    <span className=\"wp-token-subtle\">{detail.name}</span>\n                  )}\n                </div>\n              </div>\n              <div className=\"wp-item-line\">\n                <span className=\"wp-item-label\">LP Balance</span>\n                <div className=\"wp-svc-token wp-svc-token--amount\">\n                  <div className=\"wp-svc-amount\">\n                    <span className=\"wp-svc-amount-main\">\n                      {formatLp(pool.lp_balance)}\n                    </span>\n                    {lpUsd && <span className=\"wp-item-usd\">{lpUsd}</span>}\n                  </div>\n                </div>\n              </div>\n              <div className=\"wp-item-line\">\n                <span className=\"wp-item-label\">Ownership</span>\n                <span className=\"wp-line-value\">\n                  {formatOwnership(pool.pool_ownership)}\n                </span>\n              </div>\n            </div>\n            <div className=\"wp-item-tags\">\n              <span className=\"wp-tag wp-tag--muted\">\n                <span className=\"wp-tag-label\">Withdraw</span>\n                <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n              </span>\n            </div>\n          </a>\n        );\n      })}\n    </SectionCard>\n  );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { ForgePool, ForgePoolsResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const FORGE_POOLS_QUERY_KEY = \"wp:forge-pools\";\n\nexport function useForgePools(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const hasAddress = enabled && !!walletAddress;\n\n  const { data, ...rest } = useQuery<ForgePool[]>({\n    queryKey: [FORGE_POOLS_QUERY_KEY, env, walletAddress],\n    queryFn: async () => {\n      const baseUrl = await getApiBase(\"bondingCurve\", env);\n      const res = await fetch(\n        `${baseUrl}/v1/users/${walletAddress}/pools`,\n        { cache: \"no-store\" },\n      );\n      if (!res.ok) throw new Error(`forge pools fetch failed: ${res.status}`);\n      const json: ForgePoolsResponse = await res.json();\n      return json.data?.items ?? [];\n    },\n    enabled: hasAddress,\n    staleTime: 0,\n  });\n\n  const pools = data ?? [];\n\n  return {\n    pools,\n    isEmpty: pools.length === 0,\n    ...rest,\n  };\n}\n","import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type {\n  ForgeTokenDetail,\n  ForgeTokenDetailResponse,\n} from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const FORGE_TOKEN_DETAIL_QUERY_KEY = \"wp:forge-token-detail\";\n\nasync function fetchForgeTokenDetail(\n  baseUrl: string,\n  tokenAddress: string,\n): Promise<ForgeTokenDetail | null> {\n  const res = await fetch(`${baseUrl}/v1/tokens/${tokenAddress}`, {\n    cache: \"no-store\",\n  });\n  if (!res.ok) return null;\n  const json: ForgeTokenDetailResponse = await res.json();\n  return json?.data?.token ?? null;\n}\n\nexport function useForgeTokenDetails(\n  env: Environment,\n  tokenAddresses: string[],\n  enabled = true,\n) {\n  const results = useQueries({\n    queries: tokenAddresses.map((addr) => ({\n      queryKey: [FORGE_TOKEN_DETAIL_QUERY_KEY, env, addr],\n      queryFn: async () =>\n        fetchForgeTokenDetail(await getApiBase(\"bondingCurve\", env), addr),\n      enabled: enabled && !!addr,\n      staleTime: 30_000,\n    })),\n  });\n\n  const detailMap = useMemo(() => {\n    const map = new Map<string, ForgeTokenDetail>();\n    for (let i = 0; i < results.length; i++) {\n      const data = results[i]?.data;\n      const addr = tokenAddresses[i];\n      if (data && addr) map.set(addr.toLowerCase(), data);\n    }\n    return map;\n  }, [results, tokenAddresses]);\n\n  const isLoading = results.some((r) => r.isLoading);\n\n  return { detailMap, isLoading };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport BigNumber from \"bignumber.js\";\nimport {\n  useWalletPortfolioContext,\n  usePortfolioFiat,\n} from \"../../providers/wallet-portfolio-provider\";\nimport { useCrossdPositions } from \"../../queries/use-crossd-positions\";\nimport { useCrossdPositionDetails } from \"../../queries/use-crossd-position-details\";\nimport { useTokenInfo } from \"../../queries/use-token-info\";\nimport { APP_LAUNCHER_ICONS_BASE, CROSSD_WEB } from \"../../constants\";\nimport { displayCrossdSymbol, formatFiatFromUsd } from \"../../utils\";\nimport { SectionCard } from \"./SectionCard\";\nimport { CrossdWithdrawDialog } from \"./CrossdWithdrawDialog\";\nimport { useCrossdWithdrawFlow } from \"./useCrossdWithdrawFlow\";\nimport { ExternalLinkIcon } from \"../icons\";\nimport { handleOutlinkClick } from \"../../../../lib/outlink\";\n\nconst SECTION_TITLE = \"ONEusd Pool\";\n\n// NOTE: CDN에 `CROSS.svg`는 없다(403) — 실존 파일은 `Cross.svg`.\nconst FALLBACK_TOKEN_ICON =\n  \"https://contents.crosstoken.io/wallet/token/images/Cross.svg\";\n\n/**\n * CROSSD v3 pool 포지션 섹션. `ForgeSection`/`PointsSection` 구조를 그대로\n * 따른다 — 항목 나열은 `wp-item`, Withdraw는 `sendTransaction` 주입 여부에\n * 따라 내장 다이얼로그(`CrossdWithdrawDialog`) 또는 CROSSD 풀 웹 아웃링크로\n * 분기한다. 실수 클릭으로 트랜잭션 다이얼로그가 뜨지 않도록 Withdraw는\n * 행 전체 링크가 아니라 명시적 버튼/링크로 둔다.\n */\nexport function CrossdPoolSection() {\n  const { env, walletAddress, onOutlink } = useWalletPortfolioContext();\n  const fiat = usePortfolioFiat();\n  const hasAddress = !!walletAddress;\n  const { positions, isLoading } = useCrossdPositions(\n    env,\n    walletAddress,\n    hasAddress,\n  );\n\n  const flow = useCrossdWithdrawFlow();\n\n  // 이번 세션에서 출금 성공한 포지션은 인덱서 반영 전이라도 즉시 숨긴다.\n  const visiblePositions = useMemo(\n    () =>\n      positions.filter(\n        (p) => !p.token_id || !flow.withdrawnTokenIds.has(p.token_id),\n      ),\n    [positions, flow.withdrawnTokenIds],\n  );\n\n  const tokenIds = useMemo(\n    () =>\n      visiblePositions\n        .map((p) => p.token_id)\n        .filter((id): id is string => !!id),\n    [visiblePositions],\n  );\n  const { detailMap } = useCrossdPositionDetails(\n    env,\n    tokenIds,\n    hasAddress && tokenIds.length > 0,\n  );\n\n  const poolWebUrl = `${CROSSD_WEB[env]}/pool`;\n\n  // 토큰 심볼/아이콘은 wallet token info API를 단일 출처로 쓴다(다른 섹션과\n  // 동일). cross-defi 쪽 심볼/logo_url은 API에 없는 토큰의 폴백으로만 사용.\n  // WCROSS는 crossd 관례대로 표시상 CROSS로 바꾼다 (트랜잭션은 원래 주소 유지).\n  const { getTokenImage, getTokenSymbol, getNativeToken } = useTokenInfo(\n    env,\n    hasAddress,\n  );\n\n  // 섹션 아이콘도 info API의 ONEUSD 토큰 이미지를 쓴다 (app-launcher icons\n  // CDN에는 oneusd 아이콘 없음). API 미로드/미등록 시 기존 crossd.png 폴백.\n  const sectionIcon =\n    getTokenImage(\"\", \"ONEUSD\", { chainOnly: true }) ||\n    `${APP_LAUNCHER_ICONS_BASE[env]}/crossd.png`;\n  // CROSSD 풀은 전부 CROSS 체인 토큰이므로 체인 한정으로만 조회한다 —\n  // 심볼 폴백이 다른 체인(BSC 등)의 동명 토큰(예: BNB 쪽 CROSS) 아이콘을\n  // 잡는 것을 막는다. stg=테스트넷/prod=메인넷 chain_id는 useTokenInfo 내부의\n  // CHAIN_IDS[env]가 이미 구분한다.\n  const CHAIN_ONLY = { chainOnly: true } as const;\n  const resolveTokenSymbol = (\n    token?: { address?: string; symbol?: string },\n    listSymbol?: string,\n  ) => {\n    const display = displayCrossdSymbol(\n      getTokenSymbol(\n        token?.address ?? \"\",\n        token?.symbol ?? listSymbol,\n        CHAIN_ONLY,\n      ),\n    );\n    // WONE(wrapped native)로 판정되면 하드코딩 표기 대신 info API의\n    // 네이티브 토큰 엔트리(sentinel 0x…0001) 심볼을 따른다 (stg=tONE,\n    // prod=ONE). API가 유일한 출처 — 엔트리 없으면 \"?\" 처리.\n    if (display === \"ONE\") {\n      return getNativeToken()?.symbol ?? \"?\";\n    }\n    return display ?? \"?\";\n  };\n  const resolveTokenIcon = (\n    token?: { address?: string; symbol?: string; logo_url?: string },\n    listSymbol?: string,\n  ) => {\n    const rawSymbol = token?.symbol ?? listSymbol;\n    const display = displayCrossdSymbol(rawSymbol);\n    // 네이티브/WONE은 cross-defi logo_url을 신뢰하지 않고 info API의\n    // 네이티브 토큰 엔트리(sentinel 0x…0001) 아이콘으로 고정한다.\n    if (display === \"ONE\") {\n      return getNativeToken()?.image || FALLBACK_TOKEN_ICON;\n    }\n    return (\n      getTokenImage(token?.address ?? \"\", display, CHAIN_ONLY) ||\n      token?.logo_url ||\n      FALLBACK_TOKEN_ICON\n    );\n  };\n\n  const targetPool = flow.target?.detail.pool;\n  const targetIcon0 = resolveTokenIcon(\n    targetPool?.token0,\n    flow.target?.position.pool?.token0_symbol,\n  );\n  const targetIcon1 = resolveTokenIcon(\n    targetPool?.token1,\n    flow.target?.position.pool?.token1_symbol,\n  );\n  const targetSymbol0 = resolveTokenSymbol(\n    targetPool?.token0,\n    flow.target?.position.pool?.token0_symbol,\n  );\n  const targetSymbol1 = resolveTokenSymbol(\n    targetPool?.token1,\n    flow.target?.position.pool?.token1_symbol,\n  );\n\n  return (\n    <>\n      <SectionCard\n        sectionKey=\"crossdPool\"\n        title={SECTION_TITLE}\n        icon={sectionIcon}\n        iconFit=\"contain\"\n        isEmpty={visiblePositions.length === 0}\n        loading={isLoading}\n      >\n        {visiblePositions.map((position) => {\n          const detail = position.token_id\n            ? detailMap.get(position.token_id)\n            : undefined;\n          const pairLabel = `${resolveTokenSymbol(\n            detail?.pool?.token0,\n            position.pool?.token0_symbol,\n          )}/${resolveTokenSymbol(\n            detail?.pool?.token1,\n            position.pool?.token1_symbol,\n          )}`;\n          const feeBadge = position.pool?.fee_tier_percentage;\n          const icon0 = resolveTokenIcon(\n            detail?.pool?.token0,\n            position.pool?.token0_symbol,\n          );\n          const icon1 = resolveTokenIcon(\n            detail?.pool?.token1,\n            position.pool?.token1_symbol,\n          );\n          const positionValueUsd = formatFiatFromUsd(\n            new BigNumber(position.position_value_usd ?? \"0\"),\n            fiat,\n          );\n          const canWithdrawInline =\n            flow.canWithdrawInline &&\n            !!detail?.pool?.token0?.address &&\n            !!detail?.pool?.token1?.address;\n\n          return (\n            <article\n              className=\"wp-item\"\n              data-side=\"crossd\"\n              key={position.token_id}\n            >\n              <div className=\"wp-item-body\">\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Pool</span>\n                  <div className=\"wp-svc-token\">\n                    <span className=\"wp-token-stack\" aria-hidden>\n                      <img className=\"wp-svc-token-icon\" src={icon0} alt=\"\" />\n                      <img className=\"wp-svc-token-icon\" src={icon1} alt=\"\" />\n                    </span>\n                    <span>{pairLabel}</span>\n                    {feeBadge && (\n                      <span className=\"wp-token-subtle\">{feeBadge}</span>\n                    )}\n                  </div>\n                </div>\n                <div className=\"wp-item-line\">\n                  <span className=\"wp-item-label\">Position Value</span>\n                  <span className=\"wp-line-value\">{positionValueUsd}</span>\n                </div>\n              </div>\n              <div className=\"wp-item-tags\">\n                {canWithdrawInline ? (\n                  <button\n                    type=\"button\"\n                    className=\"wp-tag wp-tag--muted\"\n                    data-track=\"crossd_pool_withdraw_open\"\n                    data-track-id={pairLabel}\n                    onClick={() => {\n                      if (!detail) return;\n                      flow.openWithdraw({ position, detail });\n                    }}\n                  >\n                    <span className=\"wp-tag-label\">Withdraw</span>\n                  </button>\n                ) : (\n                  <a\n                    className=\"wp-tag wp-tag--muted\"\n                    href={poolWebUrl}\n                    target=\"_blank\"\n                    rel=\"noopener noreferrer\"\n                    data-track=\"crossd_pool_outlink\"\n                    data-track-id={pairLabel}\n                    onClick={(e) =>\n                      handleOutlinkClick(\n                        e,\n                        poolWebUrl,\n                        {\n                          category: \"portfolio\",\n                          origin: \"portfolio-crossd-pool\",\n                          payload: { position, detail },\n                        },\n                        onOutlink,\n                      )\n                    }\n                  >\n                    <span className=\"wp-tag-label\">Withdraw</span>\n                    <ExternalLinkIcon size={12} className=\"wp-tag-icon\" />\n                  </a>\n                )}\n              </div>\n            </article>\n          );\n        })}\n      </SectionCard>\n\n      <CrossdWithdrawDialog\n        open={flow.isOpen}\n        status={flow.status}\n        target={flow.target}\n        token0Icon={targetIcon0}\n        token1Icon={targetIcon1}\n        token0Symbol={targetSymbol0}\n        token1Symbol={targetSymbol1}\n        nativeSymbol={getNativeToken()?.symbol}\n        txHash={flow.txHash}\n        error={flow.error}\n        copied={flow.copied}\n        hasEstimator={flow.hasEstimator}\n        prepareWithdraw={flow.prepareWithdraw}\n        confirmWithdraw={flow.confirmWithdraw}\n        onClose={flow.closeWithdraw}\n        onCopyHash={flow.copyHash}\n        trackFunnel={flow.trackFunnelOnce}\n      />\n    </>\n  );\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { CrossdPosition, CrossdPositionsResponse } from \"../types\";\nimport { CROSS_DEFI_API } from \"../constants\";\n\nexport const CROSSD_POSITIONS_QUERY_KEY = \"wp:crossd-positions\";\n\n/** 응답 envelope 유무를 방어하며 출금 가능한(liquidity>0) 포지션만 남긴다. */\nexport function parseCrossdPositions(json: unknown): CrossdPosition[] {\n  const body = (json ?? {}) as CrossdPositionsResponse;\n  const list = body.positions ?? body.data?.positions ?? [];\n  return list.filter((p) => {\n    if (!p?.token_id || !p.liquidity) return false;\n    try {\n      return BigInt(p.liquidity) > 0n;\n    } catch {\n      return false;\n    }\n  });\n}\n\nexport function useCrossdPositions(\n  env: Environment,\n  walletAddress: string,\n  enabled = true,\n) {\n  const hasAddress = enabled && !!walletAddress;\n\n  const { data, ...rest } = useQuery<CrossdPosition[]>({\n    queryKey: [CROSSD_POSITIONS_QUERY_KEY, env, walletAddress],\n    queryFn: async () => {\n      const res = await fetch(\n        `${CROSS_DEFI_API[env]}/api/v1/positions?owner=${walletAddress}`,\n        { cache: \"no-store\" },\n      );\n      if (!res.ok) {\n        throw new Error(`crossd positions fetch failed: ${res.status}`);\n      }\n      return parseCrossdPositions(await res.json());\n    },\n    enabled: hasAddress,\n    staleTime: 0,\n  });\n\n  const positions = data ?? [];\n\n  return {\n    positions,\n    isEmpty: positions.length === 0,\n    ...rest,\n  };\n}\n","import { useQueries } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { CrossdPositionDetail } from \"../types\";\nimport { CROSS_DEFI_API } from \"../constants\";\n\nexport const CROSSD_POSITION_DETAIL_QUERY_KEY = \"wp:crossd-position-details\";\n\n/** detail 응답의 envelope 유무 방어 파싱. */\nfunction parseDetail(json: unknown): CrossdPositionDetail | null {\n  const body = (json ?? {}) as { data?: CrossdPositionDetail } &\n    CrossdPositionDetail;\n  const detail = body.data ?? body;\n  return detail?.token_id || detail?.pool ? detail : null;\n}\n\n/**\n * 포지션별 detail(`GET /api/v1/positions/{token_id}`)을 병렬 조회해\n * token_id → detail 맵을 만든다. list API에는 없는 pool.token0/token1의\n * 주소·decimals(WCROSS 판별 + 수량 표기)가 목적.\n */\nexport function useCrossdPositionDetails(\n  env: Environment,\n  tokenIds: string[],\n  enabled = true,\n) {\n  const results = useQueries({\n    queries: tokenIds.map((tokenId) => ({\n      queryKey: [CROSSD_POSITION_DETAIL_QUERY_KEY, env, tokenId],\n      queryFn: async () => {\n        const res = await fetch(\n          `${CROSS_DEFI_API[env]}/api/v1/positions/${tokenId}`,\n          { cache: \"no-store\" },\n        );\n        if (!res.ok) {\n          throw new Error(`crossd position detail failed: ${res.status}`);\n        }\n        return parseDetail(await res.json());\n      },\n      enabled: enabled && !!tokenId,\n      staleTime: 60_000,\n    })),\n  });\n\n  const detailMap = useMemo(() => {\n    const map = new Map<string, CrossdPositionDetail>();\n    results.forEach((r, i) => {\n      const data = r.data;\n      const tokenId = tokenIds[i];\n      if (data && tokenId) map.set(tokenId, data);\n    });\n    return map;\n  }, [results, tokenIds]);\n\n  return { detailMap };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { CheckIcon, ClipboardIcon, SuccessCheckIcon } from \"../icons\";\nimport type { GasEstimate } from \"../../types\";\nimport {\n  formatGasLimit,\n  formatGweiFromWei,\n  formatNativeFeeFromWei,\n  rewardCardFitLevel,\n  shortenTransactionHash,\n} from \"../../utils\";\nimport { formatRawTokenAmount } from \"../../../send/utils\";\nimport type {\n  CrossdWithdrawPrepared,\n  CrossdWithdrawStatus,\n  CrossdWithdrawTarget,\n} from \"./useCrossdWithdrawFlow\";\n\n/** confirm 시점에 준비본이 이보다 오래되면 재-prepare 후 전송한다(스테일 deadline 방지). */\nconst PREPARED_STALE_MS = 5 * 60 * 1000;\n\nconst FALLBACK_TOKEN_ICON =\n  \"https://contents.crosstoken.io/wallet/token/images/Cross.svg\";\n\ninterface CrossdWithdrawDialogProps {\n  open: boolean;\n  status: CrossdWithdrawStatus;\n  target: CrossdWithdrawTarget | null;\n  /** 토큰 아이콘/심볼 (섹션이 token info API 기준으로 해석해 전달). */\n  token0Icon?: string;\n  token1Icon?: string;\n  token0Symbol?: string;\n  token1Symbol?: string;\n  /** 가스비 표시용 네이티브 토큰 심볼 (info API 기준, 예: tONE/ONE). */\n  nativeSymbol?: string;\n  txHash?: `0x${string}`;\n  error?: string;\n  copied?: boolean;\n  hasEstimator: boolean;\n  prepareWithdraw: () => Promise<CrossdWithdrawPrepared | null>;\n  confirmWithdraw: (prepared: CrossdWithdrawPrepared) => Promise<void>;\n  onClose: () => void;\n  onCopyHash: () => void;\n  trackFunnel?: (step: string) => void;\n}\n\nfunction formatFeeValue(\n  estimate: GasEstimate | null | undefined,\n  isEstimating: boolean,\n  nativeSymbol?: string,\n): string {\n  if (isEstimating) return \"Estimating...\";\n  if (!estimate) return \"Wallet estimate\";\n\n  const feePerGas = estimate.maxFeePerGas ?? estimate.gasPrice;\n  if (!feePerGas || feePerGas <= 0n || estimate.gasLimit <= 0n) {\n    return \"Wallet estimate\";\n  }\n  return `${formatNativeFeeFromWei(estimate.gasLimit * feePerGas)} ${nativeSymbol ?? \"\"}`.trim();\n}\n\nfunction formatGasMetric(\n  value: string,\n  unit: string,\n  isEstimating: boolean,\n): string {\n  if (isEstimating) return \"Estimating...\";\n  return `${value} ${unit}`.trim();\n}\n\n/**\n * CROSSD v3 포지션 전체 출금 확인 다이얼로그. `WithdrawDialog.tsx`의 시트\n * 구조(`wp-claim-*`)를 재사용하지만, 대상 포지션이 이미 확정돼 있고\n * 전량 출금만 지원하므로 토큰 선택/금액 입력이 없다. 열릴 때 1회\n * `prepareWithdraw()`로 시뮬레이션 + 가스 추정을 받아 예상 수령량을 보여주고,\n * Withdraw 클릭 시 준비본이 5분 이상 지났으면 재-prepare 후 전송한다.\n */\nexport function CrossdWithdrawDialog({\n  open,\n  status,\n  target,\n  token0Icon,\n  token1Icon,\n  token0Symbol,\n  token1Symbol,\n  nativeSymbol,\n  txHash,\n  error,\n  copied = false,\n  hasEstimator,\n  prepareWithdraw,\n  confirmWithdraw,\n  onClose,\n  onCopyHash,\n  trackFunnel,\n}: CrossdWithdrawDialogProps) {\n  const [prepared, setPrepared] = React.useState<CrossdWithdrawPrepared | null>(\n    null,\n  );\n  const [preparedAt, setPreparedAt] = React.useState<number | null>(null);\n  // open/target 조합이 바뀔 때마다 증가시켜, 닫힌 뒤(또는 재오픈된 뒤) 늦게\n  // 도착한 prepare 응답이 새 상태를 덮어쓰지 못하게 막는다.\n  const generationRef = React.useRef(0);\n\n  React.useEffect(() => {\n    if (!open || !target) return;\n    const generation = ++generationRef.current;\n    setPrepared(null);\n    setPreparedAt(null);\n    trackFunnel?.(\"dialog_opened\");\n    prepareWithdraw().then((result) => {\n      if (generationRef.current !== generation) return;\n      setPrepared(result);\n      setPreparedAt(result ? Date.now() : null);\n    });\n    // prepareWithdraw는 target에 종속된 콜백이므로 target 변경 시 함께 재실행된다.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [open, target]);\n\n  const isSubmitting = status === \"submitting\";\n  const isEstimating =\n    status === \"estimating\" || (status === \"confirm\" && !prepared);\n\n  const runPrepareAndConfirm = React.useCallback(async () => {\n    let next = prepared;\n    const isStale = !preparedAt || Date.now() - preparedAt > PREPARED_STALE_MS;\n\n    if (!next || isStale) {\n      const generation = ++generationRef.current;\n      next = await prepareWithdraw();\n      if (generationRef.current !== generation) return;\n      setPrepared(next);\n      setPreparedAt(next ? Date.now() : null);\n    }\n\n    if (!next) return; // prepareWithdraw가 이미 status=\"error\"로 남겨둔다.\n    await confirmWithdraw(next);\n  }, [prepared, preparedAt, prepareWithdraw, confirmWithdraw]);\n\n  if (!open || !target) return null;\n\n  const { position, detail } = target;\n  const token0 = detail.pool?.token0;\n  const token1 = detail.pool?.token1;\n  const symbol0 =\n    token0Symbol || token0?.symbol || position.pool?.token0_symbol || \"?\";\n  const symbol1 =\n    token1Symbol || token1?.symbol || position.pool?.token1_symbol || \"?\";\n  const decimals0 = token0?.decimals ?? 18;\n  const decimals1 = token1?.decimals ?? 18;\n  const icon0 = token0Icon || token0?.logo_url || FALLBACK_TOKEN_ICON;\n  const icon1 = token1Icon || token1?.logo_url || FALLBACK_TOKEN_ICON;\n  const pairLabel = `${symbol0}/${symbol1}`;\n\n  const expectedAmount0Display = prepared\n    ? formatRawTokenAmount(prepared.expectedAmount0.toString(), decimals0)\n    : null;\n  const expectedAmount1Display = prepared\n    ? formatRawTokenAmount(prepared.expectedAmount1.toString(), decimals1)\n    : null;\n\n  const priorityFee = formatGweiFromWei(\n    prepared?.estimate?.maxPriorityFeePerGas,\n  );\n  const maxFee = formatGweiFromWei(\n    prepared?.estimate?.maxFeePerGas ?? prepared?.estimate?.gasPrice,\n  );\n  const gasLimit = formatGasLimit(prepared?.estimate?.gasLimit);\n\n  return (\n    <div\n      className=\"wp-claim-overlay\"\n      role=\"presentation\"\n      onMouseDown={(event) => {\n        if (event.target === event.currentTarget && !isSubmitting) {\n          onClose();\n        }\n      }}\n    >\n      <section\n        className=\"wp-claim-sheet\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby=\"wp-crossd-withdraw-title\"\n        aria-label=\"Withdraw position\"\n        data-status={status}\n        data-vaul-no-drag\n      >\n        <div className=\"wp-claim-grabber\" aria-hidden>\n          <span />\n        </div>\n\n        <div className=\"wp-claim-content wp-withdraw-content\">\n          {status === \"success\" && txHash ? (\n            <>\n              <span\n                className=\"wp-claim-icon wp-claim-icon--success\"\n                aria-hidden\n              >\n                <SuccessCheckIcon />\n              </span>\n\n              <div className=\"wp-claim-copy\">\n                <h3 id=\"wp-crossd-withdraw-title\" className=\"wp-claim-title\">\n                  Withdraw Submitted!\n                </h3>\n                <p className=\"wp-claim-desc\">\n                  Your {pairLabel} position withdrawal has been submitted.\n                  Balances will update shortly after the transaction settles.\n                </p>\n              </div>\n\n              <div className=\"wp-claim-list\" aria-label=\"Withdraw transaction\">\n                <div className=\"wp-claim-list-item\">\n                  <span>Tx Hash</span>\n                  <button\n                    type=\"button\"\n                    className=\"wp-claim-hash\"\n                    onClick={onCopyHash}\n                    aria-label=\"Copy transaction hash\"\n                    data-track=\"crossd_withdraw_hash_copy\"\n                  >\n                    <strong>{shortenTransactionHash(txHash)}</strong>\n                    {copied ? <CheckIcon /> : <ClipboardIcon />}\n                  </button>\n                </div>\n              </div>\n            </>\n          ) : (\n            <>\n              {status === \"error\" && (\n                <div className=\"wp-claim-heading\">\n                  <h3 id=\"wp-crossd-withdraw-title\" className=\"wp-claim-title\">\n                    Withdraw Failed\n                  </h3>\n                  <p className=\"wp-claim-error-message\">\n                    {error || \"The withdraw request could not be completed.\"}\n                  </p>\n                </div>\n              )}\n\n              <div\n                className=\"wp-claim-reward-card\"\n                data-fit={rewardCardFitLevel(\n                  symbol0,\n                  expectedAmount0Display ?? \"\",\n                )}\n                aria-label={`Expected ${symbol0}`}\n              >\n                <span className=\"wp-claim-reward-token\" aria-hidden>\n                  <img src={icon0} alt=\"\" width={28} height={28} />\n                </span>\n                <strong className=\"wp-claim-reward-symbol\">{symbol0}</strong>\n                <strong className=\"wp-claim-reward-amount\">\n                  {expectedAmount0Display ?? \"Estimating...\"}\n                </strong>\n              </div>\n\n              <div\n                className=\"wp-claim-reward-card\"\n                data-fit={rewardCardFitLevel(\n                  symbol1,\n                  expectedAmount1Display ?? \"\",\n                )}\n                aria-label={`Expected ${symbol1}`}\n              >\n                <span className=\"wp-claim-reward-token\" aria-hidden>\n                  <img src={icon1} alt=\"\" width={28} height={28} />\n                </span>\n                <strong className=\"wp-claim-reward-symbol\">{symbol1}</strong>\n                <strong className=\"wp-claim-reward-amount\">\n                  {expectedAmount1Display ?? \"Estimating...\"}\n                </strong>\n              </div>\n\n              {hasEstimator && (\n                <div\n                  className=\"wp-claim-account-card\"\n                  aria-label=\"Withdraw gas fee\"\n                >\n                  <div className=\"wp-claim-detail-list\">\n                    <div className=\"wp-claim-detail-row\">\n                      <span>Est. Tx Fee</span>\n                      <strong>\n                        {formatFeeValue(\n                          prepared?.estimate,\n                          isEstimating,\n                          nativeSymbol,\n                        )}\n                      </strong>\n                    </div>\n                    <div className=\"wp-claim-detail-row wp-claim-detail-row--sub\">\n                      <span>Priority Fee Per Gas</span>\n                      <strong>\n                        {formatGasMetric(priorityFee, \"Gwei\", isEstimating)}\n                      </strong>\n                    </div>\n                    <div className=\"wp-claim-detail-row wp-claim-detail-row--sub\">\n                      <span>Max Fee Per Gas</span>\n                      <strong>\n                        {formatGasMetric(maxFee, \"Gwei\", isEstimating)}\n                      </strong>\n                    </div>\n                    <div className=\"wp-claim-detail-row wp-claim-detail-row--sub\">\n                      <span>Gas Limit</span>\n                      <strong>\n                        {formatGasMetric(gasLimit, \"\", isEstimating)}\n                      </strong>\n                    </div>\n                  </div>\n                </div>\n              )}\n            </>\n          )}\n\n          <div\n            className=\"wp-claim-actions\"\n            data-count={status === \"confirm\" || status === \"error\" ? 2 : 1}\n          >\n            {(status === \"confirm\" || status === \"error\") && (\n              <button\n                type=\"button\"\n                className=\"wp-claim-secondary\"\n                data-track=\"crossd_withdraw_cancel\"\n                onClick={onClose}\n              >\n                {status === \"error\" ? \"Close\" : \"Cancel\"}\n              </button>\n            )}\n            <button\n              type=\"button\"\n              className=\"wp-claim-primary\"\n              data-track=\"crossd_withdraw_confirm\"\n              onClick={() => {\n                if (status === \"success\") {\n                  onClose();\n                  return;\n                }\n                if (isSubmitting || isEstimating) return;\n                trackFunnel?.(\"confirm_clicked\");\n                void runPrepareAndConfirm();\n              }}\n              disabled={isSubmitting || isEstimating}\n            >\n              {status === \"success\" ? (\n                \"Done\"\n              ) : isSubmitting ? (\n                <span\n                  className=\"wp-claim-button-spinner\"\n                  aria-label=\"Waiting\"\n                />\n              ) : isEstimating ? (\n                <span\n                  className=\"wp-claim-button-spinner\"\n                  aria-label=\"Estimating\"\n                />\n              ) : status === \"error\" ? (\n                \"Retry\"\n              ) : (\n                \"Withdraw\"\n              )}\n            </button>\n          </div>\n        </div>\n\n        <div className=\"wp-claim-home-indicator\" aria-hidden>\n          <span />\n        </div>\n      </section>\n    </div>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { useWalletPortfolioContext } from \"../../providers/wallet-portfolio-provider\";\nimport {\n  CHAIN_IDS,\n  CROSSD_POSITION_MANAGER,\n  CROSSD_WCROSS,\n} from \"../../constants\";\nimport type {\n  CrossdPosition,\n  CrossdPositionDetail,\n  GasEstimate,\n  SendTransactionArgs,\n} from \"../../types\";\nimport {\n  DEADLINE_SECONDS,\n  applyWithdrawSlippage,\n  composeV3WithdrawCalldata,\n} from \"../../v3-position-encoding\";\nimport { simulateDecreaseLiquidity } from \"../../queries/v3-simulation-rpc\";\nimport { toDisplayWalletError } from \"../../utils\";\nimport {\n  normalizeFailureReason,\n  trackDappUiFunnel,\n  type TrackDappUiFunnelOptions,\n} from \"../../../../lib/sentry/index\";\nimport { invalidateWalletPortfolioQueries } from \"../../refresh\";\n\n/** crossd처럼 인덱서 반영 지연을 고려해 시차를 두고 재조회한다. */\nconst REFETCH_DELAYS_MS = [1_500, 3_000, 4_500, 6_000] as const;\n\nexport type CrossdWithdrawStatus =\n  | \"confirm\"\n  | \"estimating\"\n  | \"submitting\"\n  | \"success\"\n  | \"error\";\n\nexport interface CrossdWithdrawTarget {\n  position: CrossdPosition;\n  detail: CrossdPositionDetail;\n}\n\nexport interface CrossdWithdrawPrepared {\n  tx: SendTransactionArgs;\n  estimate: GasEstimate | null;\n  expectedAmount0: bigint;\n  expectedAmount1: bigint;\n}\n\nfunction toHexAddress(address: string): `0x${string}` | null {\n  return /^0x[a-fA-F0-9]{40}$/.test(address)\n    ? (address as `0x${string}`)\n    : null;\n}\n\n/**\n * CROSSD v3 포지션 전체 출금(유동성 100% 제거 + 수수료 수령) 상태 머신.\n * `useWithdrawFlow`(rewards/points)와 동일한 상태·퍼널 구조를 따르고,\n * 트랜잭션 빌드만 NonfungiblePositionManager multicall로 교체했다.\n */\nexport function useCrossdWithdrawFlow() {\n  const { env, walletAddress, sendTransaction, estimateGas } =\n    useWalletPortfolioContext();\n  const canWithdrawInline = !!sendTransaction;\n  const queryClient = useQueryClient();\n\n  const [isOpen, setIsOpen] = React.useState(false);\n  const [target, setTarget] = React.useState<CrossdWithdrawTarget | null>(null);\n  const [status, setStatus] = React.useState<CrossdWithdrawStatus>(\"confirm\");\n  const [txHash, setTxHash] = React.useState<`0x${string}` | undefined>();\n  const [error, setError] = React.useState(\"\");\n  const [copied, setCopied] = React.useState(false);\n  /**\n   * 이번 세션에서 출금 성공한 포지션 token_id들. cross-defi 인덱서가 반영을\n   * 따라잡기 전까지 API가 계속 내려주는 stale 포지션을 리스트에서 즉시\n   * 숨기기 위한 낙관적 마킹 — API가 따라잡으면 자연히 무의미해진다.\n   */\n  const [withdrawnTokenIds, setWithdrawnTokenIds] = React.useState<\n    ReadonlySet<string>\n  >(new Set());\n  const copyTimerRef = React.useRef<ReturnType<typeof setTimeout>>(undefined);\n  const refetchTimersRef = React.useRef<ReturnType<typeof setTimeout>[]>([]);\n  /** open/close마다 증가시켜, 닫힌 뒤 늦게 끝난 prepareWithdraw가 새 상태를 덮어쓰지 못하게 막는다. */\n  const flowIdRef = React.useRef(0);\n\n  React.useEffect(() => {\n    return () => {\n      clearTimeout(copyTimerRef.current);\n      refetchTimersRef.current.forEach(clearTimeout);\n    };\n  }, []);\n\n  const funnelFiredRef = React.useRef(new Set<string>());\n  const trackFunnelOnce = React.useCallback(\n    (funnelStep: string, funnelOptions?: TrackDappUiFunnelOptions) => {\n      if (funnelFiredRef.current.has(funnelStep)) return;\n      funnelFiredRef.current.add(funnelStep);\n      trackDappUiFunnel(\"withdraw\", funnelStep, {\n        ...funnelOptions,\n        tags: { section: \"crossdPool\", ...funnelOptions?.tags },\n      });\n    },\n    [],\n  );\n\n  const invalidatePortfolioQueries = React.useCallback(() => {\n    const invalidate = () => invalidateWalletPortfolioQueries(queryClient);\n    invalidate();\n    refetchTimersRef.current.forEach(clearTimeout);\n    refetchTimersRef.current = REFETCH_DELAYS_MS.map((delay) =>\n      setTimeout(invalidate, delay),\n    );\n  }, [queryClient]);\n\n  const openWithdraw = React.useCallback(\n    (next: CrossdWithdrawTarget) => {\n      flowIdRef.current += 1;\n      setTarget(next);\n      setIsOpen(true);\n      setStatus(\"confirm\");\n      setTxHash(undefined);\n      setError(\"\");\n      setCopied(false);\n      funnelFiredRef.current = new Set();\n      trackFunnelOnce(\"opened\");\n    },\n    [trackFunnelOnce],\n  );\n\n  /** 전송 중(submitting)에만 닫기를 막는다. estimating은 시뮬레이션/가스 추정 대기일 뿐이라 취소 가능해야 한다. */\n  const closeWithdraw = React.useCallback(() => {\n    if (status === \"submitting\") return;\n    flowIdRef.current += 1;\n    setIsOpen(false);\n    setTarget(null);\n    setStatus(\"confirm\");\n    setTxHash(undefined);\n    setError(\"\");\n    setCopied(false);\n  }, [status]);\n\n  /**\n   * 시뮬레이션 → 슬리피지 1% → multicall calldata → 가스 추정.\n   * 실패 시 status=\"error\"로 두고 null 반환 — 호출부는 전송하지 않는다.\n   */\n  const prepareWithdraw =\n    React.useCallback(async (): Promise<CrossdWithdrawPrepared | null> => {\n      const flowId = flowIdRef.current;\n      const owner = toHexAddress(walletAddress);\n      const tokenIdRaw = target?.position.token_id;\n      const liquidityRaw = target?.position.liquidity;\n      const token0 = target?.detail.pool?.token0?.address;\n      const token1 = target?.detail.pool?.token1?.address;\n\n      if (!owner || !tokenIdRaw || !liquidityRaw || !token0 || !token1) {\n        setStatus(\"error\");\n        setError(\"Position data is incomplete. Please refresh and retry.\");\n        return null;\n      }\n\n      try {\n        setStatus(\"estimating\");\n        setError(\"\");\n\n        const tokenId = BigInt(tokenIdRaw);\n        const liquidity = BigInt(liquidityRaw);\n        const deadline =\n          BigInt(Math.floor(Date.now() / 1000)) + DEADLINE_SECONDS;\n        const positionManager = CROSSD_POSITION_MANAGER[env];\n\n        const simulated = await simulateDecreaseLiquidity(env, {\n          owner,\n          positionManager,\n          tokenId,\n          liquidity,\n          deadline,\n        });\n        if (flowIdRef.current !== flowId) return null; // 대기 중 닫힘/재오픈됨 — 새 플로우 상태를 건드리지 않는다.\n        if (!simulated) {\n          setStatus(\"error\");\n          setError(\"Failed to estimate withdrawal amounts. Please retry.\");\n          return null;\n        }\n\n        const data = composeV3WithdrawCalldata({\n          tokenId,\n          liquidity,\n          amount0Min: applyWithdrawSlippage(simulated.amount0),\n          amount1Min: applyWithdrawSlippage(simulated.amount1),\n          deadline,\n          owner,\n          positionManagerAddress: positionManager,\n          token0,\n          token1,\n          wcrossAddress: CROSSD_WCROSS[env],\n        });\n\n        const tx: SendTransactionArgs = {\n          to: positionManager,\n          value: 0n,\n          data,\n          chainId: CHAIN_IDS[env].cross,\n        };\n\n        let estimate: GasEstimate | null = null;\n        if (estimateGas) {\n          try {\n            estimate = await estimateGas({ ...tx, from: owner });\n          } catch {\n            estimate = null; // 추정 실패는 지갑 추정에 위임\n          }\n          if (flowIdRef.current !== flowId) return null; // 대기 중 닫힘/재오픈됨 — 새 플로우 상태를 건드리지 않는다.\n        }\n\n        setStatus(\"confirm\");\n        return {\n          tx,\n          estimate,\n          expectedAmount0: simulated.amount0,\n          expectedAmount1: simulated.amount1,\n        };\n      } catch (err) {\n        if (flowIdRef.current !== flowId) return null; // 대기 중 닫힘/재오픈됨 — 새 플로우 상태를 건드리지 않는다.\n        setStatus(\"error\");\n        setError(toDisplayWalletError(err, \"Failed to prepare withdraw.\"));\n        return null;\n      }\n    }, [env, estimateGas, target, walletAddress]);\n\n  const confirmWithdraw = React.useCallback(\n    async (prepared: CrossdWithdrawPrepared) => {\n      if (!sendTransaction) {\n        setStatus(\"error\");\n        setError(\"Withdraw is not available for this wallet.\");\n        return;\n      }\n\n      try {\n        setStatus(\"submitting\");\n        setError(\"\");\n        trackFunnelOnce(\"submitted\");\n\n        const hash = await sendTransaction({\n          ...prepared.tx,\n          gas:\n            prepared.estimate?.gasLimit && prepared.estimate.gasLimit > 0n\n              ? prepared.estimate.gasLimit\n              : undefined,\n          maxFeePerGas:\n            prepared.estimate?.maxFeePerGas &&\n            prepared.estimate.maxFeePerGas > 0n\n              ? prepared.estimate.maxFeePerGas\n              : undefined,\n          maxPriorityFeePerGas:\n            prepared.estimate?.maxPriorityFeePerGas &&\n            prepared.estimate.maxPriorityFeePerGas > 0n\n              ? prepared.estimate.maxPriorityFeePerGas\n              : undefined,\n        });\n\n        setTxHash(hash);\n        setStatus(\"success\");\n        const withdrawnTokenId = target?.position.token_id;\n        if (withdrawnTokenId) {\n          setWithdrawnTokenIds((prev) => {\n            const next = new Set(prev);\n            next.add(withdrawnTokenId);\n            return next;\n          });\n        }\n        trackFunnelOnce(\"confirmed\");\n        invalidatePortfolioQueries();\n      } catch (err) {\n        setStatus(\"error\");\n        setError(toDisplayWalletError(err, \"Failed to request withdraw.\"));\n        trackFunnelOnce(\"confirmed\", {\n          status: \"failure\",\n          reason: normalizeFailureReason(err),\n        });\n      }\n    },\n    [invalidatePortfolioQueries, sendTransaction, target, trackFunnelOnce],\n  );\n\n  const copyHash = React.useCallback(async () => {\n    if (!txHash || typeof navigator === \"undefined\" || !navigator.clipboard) {\n      return;\n    }\n    try {\n      await navigator.clipboard.writeText(txHash);\n      setCopied(true);\n      clearTimeout(copyTimerRef.current);\n      copyTimerRef.current = setTimeout(() => setCopied(false), 1_400);\n    } catch {\n      setCopied(false);\n    }\n  }, [txHash]);\n\n  return {\n    canWithdrawInline,\n    isOpen,\n    target,\n    withdrawnTokenIds,\n    status,\n    txHash,\n    error,\n    copied,\n    hasEstimator: !!estimateGas,\n    openWithdraw,\n    closeWithdraw,\n    prepareWithdraw,\n    confirmWithdraw,\n    copyHash,\n    trackFunnelOnce,\n  };\n}\n","/**\n * CROSSD v3 pool(NonfungiblePositionManager) 전체 출금 트랜잭션의 순수 ABI 인코딩.\n * dapp-ui는 viem/wagmi를 직접 쓰지 못하므로 수동 인코딩한다.\n * 셀렉터/호출 구성은 crossd-frontend `use-position-manager.ts` /\n * `use-remove-liquidity.ts`(remove 100% 경로) 준거.\n * 모든 wei-scale 값은 BigInt로 처리한다 (CLAUDE.md NUMERIC_HANDLING).\n */\n\n/** `decreaseLiquidity((uint256,uint128,uint256,uint256,uint256))` */\nconst DECREASE_LIQUIDITY_SELECTOR = \"0x0c49ccbe\";\n/** `collect((uint256,address,uint128,uint128))` */\nconst COLLECT_SELECTOR = \"0xfc6f7865\";\n/** `unwrapWETH9(uint256,address)` */\nconst UNWRAP_WETH9_SELECTOR = \"0x49404b7c\";\n/** `sweepToken(address,uint256,address)` */\nconst SWEEP_TOKEN_SELECTOR = \"0xdf2ab5bb\";\n/** `multicall(bytes[])` */\nconst MULTICALL_SELECTOR = \"0xac9650d8\";\n\n/** collect의 amount0Max/amount1Max — 미수령분 전부 수령. */\nexport const MAX_UINT128 = (1n << 128n) - 1n;\n/** 트랜잭션 deadline 여유 (crossd와 동일: 10분). */\nexport const DEADLINE_SECONDS = 600n;\n\nconst SLIPPAGE_NUMERATOR = 99n;\nconst SLIPPAGE_DENOMINATOR = 100n;\n\n/** 시뮬레이션 수량에 고정 1% 슬리피지를 적용한 최소 수령량 (BigInt floor). */\nexport function applyWithdrawSlippage(amount: bigint): bigint {\n  return (amount * SLIPPAGE_NUMERATOR) / SLIPPAGE_DENOMINATOR;\n}\n\nexport function isSameAddress(a?: string, b?: string): boolean {\n  return !!a && !!b && a.toLowerCase() === b.toLowerCase();\n}\n\nfunction uintWord(value: bigint, label: string): string {\n  if (value < 0n) throw new Error(`${label} must be non-negative`);\n  const hex = value.toString(16);\n  if (hex.length > 64) throw new Error(`${label} exceeds uint256`);\n  return hex.padStart(64, \"0\");\n}\n\nfunction addressWord(address: string, label: string): string {\n  if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {\n    throw new Error(`${label} is not a valid address`);\n  }\n  return address.toLowerCase().replace(/^0x/, \"\").padStart(64, \"0\");\n}\n\nexport interface DecreaseLiquidityParams {\n  tokenId: bigint;\n  liquidity: bigint;\n  amount0Min: bigint;\n  amount1Min: bigint;\n  deadline: bigint;\n}\n\nexport function encodeDecreaseLiquidity(\n  p: DecreaseLiquidityParams,\n): `0x${string}` {\n  return (DECREASE_LIQUIDITY_SELECTOR +\n    uintWord(p.tokenId, \"tokenId\") +\n    uintWord(p.liquidity, \"liquidity\") +\n    uintWord(p.amount0Min, \"amount0Min\") +\n    uintWord(p.amount1Min, \"amount1Min\") +\n    uintWord(p.deadline, \"deadline\")) as `0x${string}`;\n}\n\nexport interface CollectParams {\n  tokenId: bigint;\n  recipient: string;\n  amount0Max: bigint;\n  amount1Max: bigint;\n}\n\nexport function encodeCollect(p: CollectParams): `0x${string}` {\n  return (COLLECT_SELECTOR +\n    uintWord(p.tokenId, \"tokenId\") +\n    addressWord(p.recipient, \"recipient\") +\n    uintWord(p.amount0Max, \"amount0Max\") +\n    uintWord(p.amount1Max, \"amount1Max\")) as `0x${string}`;\n}\n\nexport function encodeUnwrapWeth9(\n  amountMinimum: bigint,\n  recipient: string,\n): `0x${string}` {\n  return (UNWRAP_WETH9_SELECTOR +\n    uintWord(amountMinimum, \"amountMinimum\") +\n    addressWord(recipient, \"recipient\")) as `0x${string}`;\n}\n\nexport function encodeSweepToken(\n  token: string,\n  amountMinimum: bigint,\n  recipient: string,\n): `0x${string}` {\n  return (SWEEP_TOKEN_SELECTOR +\n    addressWord(token, \"token\") +\n    uintWord(amountMinimum, \"amountMinimum\") +\n    addressWord(recipient, \"recipient\")) as `0x${string}`;\n}\n\n/**\n * `multicall(bytes[])` 동적 인코딩.\n * 레이아웃: [배열 오프셋(0x20)][원소 수 N][원소별 상대 오프셋 ×N]\n * [원소별 (길이 워드 + 32바이트 패딩된 데이터)].\n * 원소 오프셋은 오프셋 테이블 시작 기준 상대값 (첫 원소 = N×32).\n */\nexport function encodeMulticall(\n  calls: readonly `0x${string}`[],\n): `0x${string}` {\n  if (calls.length === 0) {\n    throw new Error(\"multicall requires at least one call\");\n  }\n  const bodies = calls.map((call) => {\n    const raw = call.replace(/^0x/, \"\");\n    if (raw.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(raw)) {\n      throw new Error(\"multicall element must be valid hex bytes\");\n    }\n    const padded =\n      raw.length % 64 === 0\n        ? raw\n        : raw.padEnd(raw.length + (64 - (raw.length % 64)), \"0\");\n    return uintWord(BigInt(raw.length / 2), \"callLength\") + padded.toLowerCase();\n  });\n\n  let offset = BigInt(calls.length) * 32n;\n  const offsets: string[] = [];\n  for (const body of bodies) {\n    offsets.push(uintWord(offset, \"callOffset\"));\n    offset += BigInt(body.length / 2);\n  }\n\n  return (MULTICALL_SELECTOR +\n    uintWord(0x20n, \"arrayOffset\") +\n    uintWord(BigInt(calls.length), \"arrayLength\") +\n    offsets.join(\"\") +\n    bodies.join(\"\")) as `0x${string}`;\n}\n\n/**\n * `decreaseLiquidity` eth_call 시뮬레이션 결과 `(uint256 amount0, uint256 amount1)`\n * 디코드. 빈 값/짧은 값/비 hex는 null.\n */\nexport function decodeDecreaseLiquidityResult(\n  hex: string | undefined,\n): { amount0: bigint; amount1: bigint } | null {\n  if (!hex || hex === \"0x\") return null;\n  const raw = hex.replace(/^0x/, \"\");\n  if (raw.length < 128 || !/^[0-9a-fA-F]+$/.test(raw)) return null;\n  return {\n    amount0: BigInt(`0x${raw.slice(0, 64)}`),\n    amount1: BigInt(`0x${raw.slice(64, 128)}`),\n  };\n}\n\nexport interface ComposeV3WithdrawInput {\n  tokenId: bigint;\n  liquidity: bigint;\n  amount0Min: bigint;\n  amount1Min: bigint;\n  deadline: bigint;\n  owner: string;\n  positionManagerAddress: string;\n  token0: string;\n  token1: string;\n  wcrossAddress: string;\n}\n\n/**\n * 전체 출금 multicall calldata 조립 (crossd remove-liquidity 100% 경로와 동일).\n * - WCROSS 미포함: [decreaseLiquidity, collect(recipient=owner)]\n * - WCROSS 포함:   [decreaseLiquidity, collect(recipient=NPM),\n *                   unwrapWETH9(0, owner), sweepToken(비WCROSS 토큰, 0, owner)]\n *   (unwrap/sweep의 amountMinimum 0 — 슬리피지는 decreaseLiquidity min으로 보장)\n */\nexport function composeV3WithdrawCalldata(\n  input: ComposeV3WithdrawInput,\n): `0x${string}` {\n  const token0IsWcross = isSameAddress(input.token0, input.wcrossAddress);\n  const token1IsWcross = isSameAddress(input.token1, input.wcrossAddress);\n  const useNativeCross = token0IsWcross || token1IsWcross;\n\n  const calls: `0x${string}`[] = [\n    encodeDecreaseLiquidity({\n      tokenId: input.tokenId,\n      liquidity: input.liquidity,\n      amount0Min: input.amount0Min,\n      amount1Min: input.amount1Min,\n      deadline: input.deadline,\n    }),\n    encodeCollect({\n      tokenId: input.tokenId,\n      recipient: useNativeCross ? input.positionManagerAddress : input.owner,\n      amount0Max: MAX_UINT128,\n      amount1Max: MAX_UINT128,\n    }),\n  ];\n\n  if (useNativeCross) {\n    calls.push(encodeUnwrapWeth9(0n, input.owner));\n    calls.push(\n      encodeSweepToken(\n        token0IsWcross ? input.token1 : input.token0,\n        0n,\n        input.owner,\n      ),\n    );\n  }\n\n  return encodeMulticall(calls);\n}\n","import type { Environment } from \"../../../types\";\nimport {\n  decodeDecreaseLiquidityResult,\n  encodeDecreaseLiquidity,\n} from \"../v3-position-encoding\";\nimport { ethCall, fetchCrossRpcUrl } from \"./lp-balance-rpc-fallback\";\n\n/**\n * 출금 확정 전에 `decreaseLiquidity`를 RPC `eth_call`로 시뮬레이션해\n * 예상 수령량 (amount0, amount1)을 얻는다 (crossd remove-liquidity와 동일 접근).\n * min=0으로 호출하며, 반환값에 슬리피지를 적용하는 것은 호출부 책임.\n * RPC 조회/호출 실패 시 null — 호출부는 출금을 진행하지 않아야 한다.\n */\nexport async function simulateDecreaseLiquidity(\n  env: Environment,\n  args: {\n    owner: string;\n    positionManager: string;\n    tokenId: bigint;\n    liquidity: bigint;\n    deadline: bigint;\n  },\n  signal?: AbortSignal,\n): Promise<{ amount0: bigint; amount1: bigint } | null> {\n  let rpcUrl: string | undefined;\n  try {\n    rpcUrl = await fetchCrossRpcUrl(env, signal);\n  } catch {\n    rpcUrl = undefined;\n  }\n  if (!rpcUrl) return null;\n\n  const data = encodeDecreaseLiquidity({\n    tokenId: args.tokenId,\n    liquidity: args.liquidity,\n    amount0Min: 0n,\n    amount1Min: 0n,\n    deadline: args.deadline,\n  });\n  const result = await ethCall(\n    rpcUrl,\n    args.positionManager,\n    data,\n    signal,\n    args.owner,\n  );\n  return decodeDecreaseLiquidityResult(result);\n}\n","/**\n * 포트폴리오 본문에 노출 가능한 섹션 종류.\n * - `\"rewards\"`  : CROSS Rewards\n * - `\"points\"`   : CROSS Points (게임 토큰 예치 퀘스트, 내장 Withdraw)\n * - `\"staking\"`  : CROSS Staking\n * - `\"gameSwap\"` : Gametoken LP\n * - `\"forge\"`    : Forge\n * - `\"crossdPool\"` : CROSSD v3 pool 포지션, 내장 Withdraw\n *\n * `WalletPortfolioBody` / `WalletInfo`의 섹션 필터에 사용한다.\n */\nexport type PortfolioSection =\n  | \"rewards\"\n  | \"points\"\n  | \"staking\"\n  | \"gameSwap\"\n  | \"forge\"\n  | \"crossdPool\";\n\n/**\n * 섹션 필터를 지정하지 않았을 때(=전체 노출)의 기본 표시 순서.\n * 배열로 필터를 줄 때 어떤 키들이 유효한지에 대한 단일 출처(source of truth).\n */\nexport const PORTFOLIO_SECTIONS: readonly PortfolioSection[] = [\n  \"rewards\",\n  \"points\",\n  \"staking\",\n  \"gameSwap\",\n  \"forge\",\n  \"crossdPool\",\n];\n\n/**\n * 포트폴리오/Send 기능에서 트랜잭션을 실제로 서명/브로드캐스트할 때\n * 호출하는 공통 payload 타입.\n *\n * 주입되는 지갑 스택(wagmi, viem, 자체 SDK 등)에 관계없이 동일한 계약으로\n * 동작시키기 위한 최소 공통 시그니처다. wagmi의 `sendTransactionAsync`를\n * 그대로 전달해도 호환된다.\n */\nexport interface SendTransactionArgs {\n  to: `0x${string}`;\n  value?: bigint;\n  data?: `0x${string}`;\n  chainId?: number;\n  gas?: bigint;\n  maxFeePerGas?: bigint;\n  maxPriorityFeePerGas?: bigint;\n}\n\nexport type SendTransactionFn = (\n  args: SendTransactionArgs,\n) => Promise<`0x${string}`>;\n\nexport interface GetTransactionReceiptArgs {\n  hash: `0x${string}`;\n  chainId?: number;\n}\n\nexport interface TransactionReceiptResult {\n  status?: \"success\" | \"reverted\" | \"0x1\" | \"0x0\" | number | bigint | boolean;\n}\n\nexport type GetTransactionReceiptFn = (\n  args: GetTransactionReceiptArgs,\n) => Promise<TransactionReceiptResult | null | undefined>;\n\n/**\n * Send 확인 단계에서 표시할 가스/수수료 추정 정보를 가져오는 콜백.\n *\n * Send 확인 화면 표기 정책 (crossy-sdk-js `docs/06-gas-fee.md` 기준):\n * - `maxFeePerGas` 채움 → Dynamic(EIP-1559) 모드로 표기\n *   · Est. Tx Fee = `gasLimit × maxFeePerGas` (native)\n *   · Max Priority Fee = `maxPriorityFeePerGas` (Gwei)\n *   · Max Gas Fee = `maxFeePerGas` (Gwei)\n *   · Gas Limit = `gasLimit`\n * - `gasPrice`만 채움 → Legacy 모드로 표기\n *   · Est. Tx Fee = `gasLimit × gasPrice` (native)\n *   · Gas Price = `gasPrice` (Gwei)\n *   · Gas Limit = `gasLimit`\n *\n * 둘 다 채워지면 Dynamic 우선. 둘 다 비어있으면 해당 행은 \"—\"로 표시된다.\n */\nexport interface EstimateGasArgs {\n  to: `0x${string}`;\n  value?: bigint;\n  data?: `0x${string}`;\n  chainId?: number;\n  from?: `0x${string}`;\n}\n\nexport interface GasEstimate {\n  gasLimit: bigint;\n  gasPrice?: bigint;\n  maxFeePerGas?: bigint;\n  maxPriorityFeePerGas?: bigint;\n}\n\nexport type EstimateGasFn = (args: EstimateGasArgs) => Promise<GasEstimate>;\n\nexport interface TokenBalance {\n  blockNumber: number;\n  name: string;\n  symbol: string;\n  chainId: number;\n  address: string;\n  quantity: {\n    decimals: number;\n    numeric: string;\n  };\n  icon_url: string;\n}\n\nexport interface TokenBalanceResponse {\n  code: number;\n  message: string;\n  data: TokenBalance[];\n}\n\nexport interface TokenStats {\n  chain_id: number;\n  address: string;\n  price: string;\n  percent_change_24h: string;\n}\n\nexport interface TokenStatsResponse {\n  code: number;\n  message: string;\n  data: TokenStats[];\n}\n\nexport interface PoolToken {\n  address: string;\n  symbol: string;\n  name: string;\n  decimals: number;\n  price: string;\n}\n\nexport interface RewardPool {\n  pool_id: number;\n  pool_address: string;\n  pool_name: string;\n  pool_type: \"CrossPool\" | \"GamePool\";\n  pool_status: string;\n  deposit_token: PoolToken;\n  reward_tokens: PoolToken[];\n  total_deposited: string;\n  total_users: number;\n  created_block: number;\n  created_time: number;\n  last_updated_block: number;\n  last_updated_time: number;\n}\n\nexport interface RewardPoolsResponse {\n  code: number;\n  message: string;\n  data: {\n    pools: RewardPool[];\n    total: number;\n    has_next: boolean;\n    page: number;\n    page_size: number;\n  };\n}\n\nexport interface UserDepositInfo {\n  account: string;\n  pool_address: string;\n  pool_id: number;\n  pool_name: string;\n  deposited_amount: string;\n  claimable_reward: string;\n  total_rewards_claimed: string;\n  last_deposited_block: number;\n  last_updated_block: number;\n  last_updated_time: number;\n  last_withdrawn_block: number;\n}\n\nexport interface UserDepositsResponse {\n  code: number;\n  message: string;\n  data: {\n    user_deposits: UserDepositInfo[];\n    total: number;\n  };\n}\n\n/**\n * 사용자가 출금할 수 있는 예치 포지션 하나 (deposited > 0인 풀).\n * `useWithdrawPositions`가 deposits API + pools API(+온체인 메타 폴백)를\n * 합성해 만든다. 금액은 raw(wei) 문자열로 유지하고 표기 시에만 환산한다.\n */\nexport interface WithdrawPosition {\n  poolId: number;\n  poolAddress: string;\n  /** pools API가 모르는(비활성/quest) 풀은 \"Unknown\". */\n  poolType: RewardPool[\"pool_type\"] | \"Unknown\";\n  tokenSymbol: string;\n  tokenAddress: string;\n  decimals: number;\n  /** USD 단가 (pools API 제공 시에만). */\n  price?: string;\n  /** 예치 잔액 (raw, wei 문자열) */\n  depositedRaw: string;\n}\n\n/**\n * reward API `GET /v1/factory/info` 응답. 내장 Withdraw가 트랜잭션을 보낼\n * `CrossGameRewardRouter` 주소(`router_address`)를 여기서 얻는다 —\n * env별 주소 하드코딩 대신 백엔드가 단일 출처.\n */\nexport interface RewardFactoryInfo {\n  active_pool_count: number;\n  factory_address: string;\n  game_pool_implementation: string;\n  initialized_at: number;\n  next_pool_id: number;\n  pool_implementation: string;\n  router_address: string;\n  total_pool_count: number;\n  wcross_address: string;\n}\n\nexport interface RewardFactoryInfoResponse {\n  code: number;\n  message: string;\n  data: RewardFactoryInfo;\n}\n\n/* ── Game-swap LP types ── */\n\n/**\n * host(wagmi 보유 측)가 공급하는 LP 잔고 reader가 반환하는 단위 정보.\n * 금액은 모두 raw(wei) BigInt로 다루고, 표기 시에만 decimals로 환산한다.\n */\nexport interface LpBalanceInfo {\n  /** 연결 계정의 LP 토큰 잔고 `balanceOf(account)` (raw, wei) */\n  balance: bigint;\n  /** LP 토큰 총발행량 `totalSupply()` (raw, wei). 없으면 지분율 계산 불가. */\n  totalSupply?: bigint;\n  /** LP 토큰 decimals (보통 18). 없으면 18로 간주. */\n  decimals?: number;\n}\n\n/**\n * 주입형 LP 잔고 reader. host(wagmi)가 연결 계정 기준으로 주어진 pair(LP)\n * 토큰 주소들의 balanceOf / totalSupply / decimals를 읽어 반환한다.\n * 반환 맵의 key는 소문자 정규화된 pair 주소.\n *\n * dapp-ui는 이 함수 시그니처에만 의존하며 viem/wagmi를 직접 사용하지 않는다.\n * 미주입 시 dapp-ui가 CROSS 체인 RPC로 직접 읽는 내부 폴백\n * (`readLpBalancesViaRpc`)으로 동작하며, 폴백마저 실패해야 풀 정보만 표시한다.\n */\nexport type LpBalanceReaderFn = (\n  pairAddresses: string[],\n) => Promise<Record<string, LpBalanceInfo>>;\n\n/**\n * game-swap `/portfolio` pool 항목의 토큰 참조.\n * NOTE: CROSS 쪽(token_b)은 백엔드가 메타데이터 없이 내려준다 —\n * `symbol: \"\"`, `name: \"\"`, `decimals: 0`, `logo_url` 누락. 소비 측에서\n * symbol은 \"ONE\"으로, decimals는 18로 간주해야 한다 (실응답 확인 기준).\n */\nexport interface GameSwapTokenRef {\n  address: string;\n  symbol: string;\n  name: string;\n  decimals: number;\n  logo_url?: string;\n}\n\n/**\n * game-swap `GET /addresses/:address/portfolio`의 `pools[]` 항목.\n * 풀 단위 정보(예비량/가격)만 담고, 사용자별 LP 잔고는 포함하지 않는다\n * (그것은 주입형 reader의 온체인 read로 보강한다).\n */\nexport interface GameSwapPool {\n  pair_address: string;\n  token_a?: GameSwapTokenRef;\n  token_b?: GameSwapTokenRef;\n  /** raw wei */\n  reserve_a: string;\n  /** raw wei */\n  reserve_b: string;\n  /** 1 token_a = X token_b(CROSS) */\n  last_price: string;\n  /** last_price × quote_usd */\n  price_usd: string;\n  /** CROSS/USD 환율 (백엔드 신규 필드명). */\n  quote_usd?: string;\n  /** @deprecated 백엔드가 `quote_usd`로 이관. 전환기 폴백용으로만 유지. */\n  cross_usd?: string;\n  /** token_a 24h 변동률 (0.0012 = +0.12%) */\n  change_24h: string;\n}\n\n/**\n * game-swap `GET /addresses/:address/portfolio` 응답 body.\n * envelope 없이 `{ holdings, pools }`가 최상위로 내려온다\n * (cross-game-swap 레포 `TypesPortfolioResponseDTO` 및 실응답 확인 기준).\n */\nexport interface GameSwapPortfolioResponse {\n  holdings?: unknown[];\n  pools?: GameSwapPool[];\n}\n\n/* ── Staking (POSA) types ── */\n\n/**\n * POSA governance API의 staking 요약 정보. `GET /stake/:address`.\n * 금액은 wei가 아니라 CROSS 단위 십진 문자열로 내려온다\n * (예: `\"500.0000\"`, `\"0.000000000000000000\"`). 실응답 확인 기준.\n */\nexport interface StakeInfo {\n  address: string;\n  /** 현재 스테이킹된 총량 (CROSS 단위 십진 문자열) */\n  total_stake: string;\n  /** 누적/청구 가능 리워드 (CROSS 단위 십진 문자열) */\n  rewards_earned: string;\n  /** 위임(delegation) 개수 */\n  delegations: number;\n  /** 최초 스테이킹 시각 (ISO timestamp). 스테이킹 이력이 없으면 null. */\n  first_staked_at: string | null;\n  /** 최초 스테이킹 이후 경과 일수. 스테이킹 이력이 없으면 null. */\n  days_since_first_stake: number | null;\n}\n\n/** `GET /stake/:address` 응답 envelope. */\nexport interface StakeInfoResponse {\n  code: number;\n  message?: string;\n  data: StakeInfo;\n}\n\n/** POSA `GET /rewards/:address` 일자별 리워드 레코드 중 포트폴리오에서 쓰는 필드. */\nexport interface StakeRewardRecord {\n  block_timestamp: string;\n  reference_date: string;\n  /** 해당 스냅샷에 적립된 리워드 (CROSS 단위 십진 문자열) */\n  earned: string;\n  /** 누적 리워드 (CROSS 단위 십진 문자열) */\n  cumulative_earned: string;\n}\n\n/** `GET /rewards/:address` 응답 envelope. 레코드는 최신순으로 내려온다. */\nexport interface StakeRewardsResponse {\n  code: number;\n  message?: string;\n  data: {\n    data: StakeRewardRecord[];\n    limit?: number;\n    page?: number;\n    total?: number;\n    total_pages?: number;\n  };\n}\n\n/**\n * 주입형 staking 리워드 reader가 반환하는 온체인 값.\n * 금액은 모두 raw(wei) BigInt로 다루고, 표기 시에만 decimals(18)로 환산한다.\n */\nexport interface StakingRewardsInfo {\n  /**\n   * 온체인 누적 미청구 리워드 `earned(account)` (raw, wei).\n   * posa 비활성 상태에서는 lastRewardBlock 이후 블록 기반 projection을 더한\n   * 추정치(= posa 포트폴리오 \"Accrued Rewards\"와 동일 값)를 반환한다.\n   */\n  earnedWei: bigint;\n  /** 온체인 스테이킹 원금 `balanceOf(account)` (raw, wei). 개인 APR 분모. */\n  stakedWei: bigint;\n}\n\n/**\n * 주입형 staking 리워드 reader. host(wagmi 보유 측)가 연결 계정 기준으로\n * POSA delegation pool 컨트랙트의 `earned()` / `balanceOf()`를 읽어 반환한다.\n * dapp-ui는 viem/wagmi를 직접 쓰지 못하므로, posa 포트폴리오의 온체인\n * \"Accrued Rewards\"와 동일한 수치를 표기하려면 이 reader가 주입돼야 한다.\n * 미주입 시 StakeSection은 API `cumulative_earned`로 폴백한다.\n *\n * dapp-ui는 이 함수 시그니처에만 의존한다.\n */\nexport type StakingRewardsReaderFn = (\n  account: string,\n) => Promise<StakingRewardsInfo>;\n\n/** 네트워크 통계 중 포트폴리오에서 사용하는 필드 (`GET /network/stats`). */\nexport interface NetworkStats {\n  /** 네트워크 APR (백분율 문자열, 예: \"12.5\") */\n  apr: string;\n  total_staked: string;\n  active_validators: number;\n}\n\n/** `GET /network/stats` 응답 envelope. */\nexport interface NetworkStatsResponse {\n  code: number;\n  message?: string;\n  data: NetworkStats;\n}\n\n/* ── Forge pair (LP) types ── */\n\nexport interface ForgePoolToken {\n  address: string;\n  name: string;\n  symbol: string;\n  image: string;\n}\n\nexport interface ForgePool {\n  lp_balance: string;\n  pair_address: string;\n  pool_ownership: string;\n  total_supply: string;\n  token: ForgePoolToken;\n}\n\nexport interface ForgePoolsResponse {\n  success?: boolean;\n  data: {\n    items: ForgePool[];\n    pagination?: {\n      page: number;\n      limit: number;\n      total: number;\n    };\n  };\n}\n\nexport interface ForgeTokenDetail {\n  address: string;\n  name: string;\n  symbol: string;\n  image: string;\n  image_url: string;\n  pair_address: string;\n  wrapped_native: string;\n  virtual_reserve_b: string;\n  reserve_a: string;\n  reserve_b: string;\n  graduated: boolean;\n  current_price: string;\n  market_cap: string;\n  total_supply: string;\n  available_supply: string;\n}\n\nexport interface ForgeTokenDetailResponse {\n  success?: boolean;\n  data: {\n    token: ForgeTokenDetail;\n  };\n}\n\n/* ── CROSSD v3 pool types ── */\n\n/** cross-defi API 토큰 참조 (detail 응답의 pool.token0/token1). */\nexport interface CrossdPositionTokenRef {\n  address?: string;\n  symbol?: string;\n  name?: string;\n  decimals?: number;\n  logo_url?: string;\n}\n\n/** positions list 항목의 pool 요약 — 심볼만 있고 토큰 주소는 없다(detail에서 보강). */\nexport interface CrossdPositionPoolRef {\n  pool_address?: string;\n  fee_tier?: number;\n  fee_tier_percentage?: string;\n  token0_symbol?: string;\n  token1_symbol?: string;\n}\n\n/**\n * cross-defi `GET /api/v1/positions?owner=` 목록 항목 중 포트폴리오가 쓰는 필드.\n * 수량류(liquidity, *_amount, uncollected_*)는 십진 문자열로 내려온다 —\n * 계산에 쓰는 것은 liquidity(BigInt 변환)뿐이고 나머지는 표시 전용.\n */\nexport interface CrossdPosition {\n  token_id?: string;\n  /** 0이면 전량 출금된(closed) 포지션 — 섹션에서 제외. */\n  liquidity?: string;\n  in_range?: boolean;\n  is_full_range?: boolean;\n  token0_amount?: string;\n  token1_amount?: string;\n  uncollected_fees_token0?: string;\n  uncollected_fees_token1?: string;\n  uncollected_fees_usd?: string;\n  position_value_usd?: string;\n  pool?: CrossdPositionPoolRef;\n}\n\nexport interface CrossdPositionsResponse {\n  positions?: CrossdPosition[];\n  /** 백엔드가 envelope로 감싸는 경우 대비한 방어적 형태. */\n  data?: { positions?: CrossdPosition[] };\n}\n\n/**\n * `GET /api/v1/positions/{token_id}` detail 중 출금에 필요한 부분.\n * list에는 없는 token0/token1의 주소·decimals를 여기서 얻는다\n * (WCROSS 판별 + sweepToken 인자 + 수량 표기).\n */\nexport interface CrossdPositionDetail {\n  token_id?: string;\n  liquidity?: string;\n  pool?: {\n    pool_address?: string;\n    token0?: CrossdPositionTokenRef;\n    token1?: CrossdPositionTokenRef;\n  };\n}\n\nexport interface CrossdPositionDetailResponse {\n  data?: CrossdPositionDetail;\n}\n","import styleInject from '#style-inject';styleInject(\".wp-fullscreen[data-theme=dark],.wp-embed[data-theme=dark]{--wp-bg: var(--ds-bg-default, #202530);--wp-header-bg: var(--ds-surface-default, #252b39);--wp-card: var(--ds-surface-medium, #252b39);--wp-control: #292f40;--wp-border: var(--ds-border-default, #292f40);--wp-panel-border: var(--ds-border-medium, rgba(255, 255, 255, .18));--wp-panel-shadow: 0px 4px 30px -1px rgba(0, 0, 0, .28);--wp-text: var(--ds-content-highest, #ffffff);--wp-text-60: var(--ds-content-high, rgba(255, 255, 255, .85));--wp-text-40: var(--ds-content-medium, rgba(255, 255, 255, .65));--wp-text-30: var(--ds-content-low, rgba(255, 255, 255, .4));--wp-chrome-text: #ffffff;--wp-tooltip-bg: #151a23;--wp-tooltip-text: #ffffff;--wp-mint: var(--ds-accent-primary-high, #f0640d);--wp-green: var(--ds-system-green-high, #00a0a2);--wp-accent-soft: var(--ds-accent-primary-lowest, #362519);--wp-red: var(--ds-system-red-default, #e70077);--wp-blue: #0488fc;--wp-purple: #7346f3;--wp-orange: #ff9d00;--wp-claim-primary: #ed0a72;--wp-claim-primary-text: #ffffff;--wp-claim-secondary-bg: var(--wp-control);--wp-claim-success: #00d4aa;--wp-claim-success-soft: rgba(0, 212, 170, .18);--wp-claim-error: #ff3aa3;--wp-claim-error-soft: rgba(255, 58, 163, .16)}.wp-fullscreen[data-theme=light],.wp-embed[data-theme=light]{--wp-bg: var(--ds-bg-dim, #f5f7fa);--wp-header-bg: var(--ds-surface-default, #ffffff);--wp-card: var(--ds-surface-default, #ffffff);--wp-control: var(--ds-surface-medium, #f5f7fa);--wp-border: var(--ds-border-default, rgba(0, 0, 0, .07));--wp-panel-border: var(--ds-border-medium, rgba(0, 0, 0, .18));--wp-panel-shadow: 0px 4px 30px -1px rgba(0, 0, 0, .15);--wp-text: var(--ds-content-highest, #000000);--wp-text-60: var(--ds-content-high, rgba(0, 0, 0, .85));--wp-text-40: var(--ds-content-medium, rgba(0, 0, 0, .65));--wp-text-30: var(--ds-content-low, rgba(0, 0, 0, .4));--wp-chrome-text: var(--ds-content-high, rgba(0, 0, 0, .85));--wp-card-shadow: none;--wp-tooltip-bg: #192023;--wp-tooltip-text: #ffffff;--wp-mint: var(--ds-accent-primary-high, #f0640d);--wp-green: var(--ds-system-green-high, #00a0a2);--wp-accent-soft: var(--ds-accent-primary-low, #fff5ee);--wp-red: var(--ds-system-red-default, #e70077);--wp-blue: #0488fc;--wp-purple: #7346f3;--wp-orange: #ff9d00;--wp-claim-primary: #ed0a72;--wp-claim-primary-text: #ffffff;--wp-claim-secondary-bg: #ffffff;--wp-claim-success: #019d92;--wp-claim-success-soft: rgba(1, 157, 146, .15);--wp-claim-error: #e70077;--wp-claim-error-soft: rgba(231, 0, 119, .12)}.wp-fullscreen{position:fixed!important;inset:0!important;width:100%!important;height:100dvh!important;max-height:100dvh!important;z-index:50;background:var(--wp-bg);display:flex;flex-direction:column;overflow:hidden;font-family:inherit;font-feature-settings:\\\"calt\\\" 0,\\\"lnum\\\" 1,\\\"pnum\\\" 1;box-sizing:border-box;-webkit-font-smoothing:antialiased;color:var(--wp-chrome-text)}.wp-embed{width:100%;height:100%;min-height:0;background:var(--wp-bg);display:flex;flex-direction:column;overflow:hidden;position:relative;font-family:inherit;font-feature-settings:\\\"calt\\\" 0,\\\"lnum\\\" 1,\\\"pnum\\\" 1;box-sizing:border-box;-webkit-font-smoothing:antialiased;color:var(--wp-chrome-text)}.wp-overlay{position:fixed;inset:0;z-index:49;background:#0009}.wp-header{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:73px;padding:24px;position:relative;border-bottom:1px solid var(--wp-border);background:var(--wp-header-bg);color:var(--wp-chrome-text);flex-shrink:0;box-sizing:border-box}.wp-fullscreen .wp-header{height:60px;min-height:60px;padding:11px 20px;border-bottom:none;background:transparent}.wp-fullscreen .wp-header-btn{width:22px;height:22px;color:var(--wp-chrome-text)}.wp-fullscreen .wp-header-btn:hover{color:var(--wp-chrome-text)}.wp-header-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;background:transparent;cursor:pointer;color:var(--ds-content-low, rgba(0, 0, 0, .4));flex-shrink:0;transition:color .15s ease}.wp-header-btn:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wp-header-edge{display:flex;align-items:center;width:24px;min-width:24px}.wp-header-edge--right{justify-content:flex-end}.wp-header-edge--right .wp-header-btn{position:absolute;top:24px;right:24px}.wp-header-spacer{display:block;width:24px;height:24px}.wp-header-avatar{width:38px;height:38px;flex-shrink:0;border-radius:50%;overflow:hidden;display:inline-flex;align-items:center;justify-content:center;background:var(--wp-header-avatar-bg)}.wp-header-avatar-img{width:30px;height:30px;object-fit:cover;display:block}.wp-fullscreen[data-theme=dark] .wp-header-avatar,.wp-embed[data-theme=dark] .wp-header-avatar{--wp-header-avatar-bg: hsl(221.3 24.6% 25.5%)}.wp-fullscreen[data-theme=light] .wp-header-avatar,.wp-embed[data-theme=light] .wp-header-avatar{--wp-header-avatar-bg: rgba(18, 18, 18, .1)}.wp-refresh-btn{position:relative}.wp-refresh-badge{position:absolute;top:-6px;right:-8px;min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--wp-mint);color:#fff;font-size:10px;font-weight:600;line-height:16px;text-align:center}.wp-spin-once{animation:wp-spin-once .6s ease-in-out}@keyframes wp-spin-once{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.wp-header-title{flex:1;font-family:inherit;font-size:18px;font-weight:600;line-height:1.5;letter-spacing:-.01em;text-align:center;color:var(--ds-content-highest, #000000);margin:0}.wp-header-account{flex:1;display:flex;align-items:center;gap:12px;min-width:0}.wp-header-account-meta{display:flex;flex-direction:column;justify-content:center;min-width:0;gap:2px}.wp-header-wallet-name{min-width:0;font-family:inherit;font-size:15px;font-weight:600;line-height:1.4;letter-spacing:0;color:var(--wp-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wp-header-address-btn{display:inline-flex;align-items:center;gap:4px;align-self:flex-start;max-width:100%;padding:0;border:0;background:transparent;cursor:pointer;font:inherit}.wp-header-address{min-width:0;font-size:12px;font-weight:400;line-height:1.4;letter-spacing:0;color:var(--wp-text-40);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wp-header-address-copy{display:inline-flex;align-items:center;flex-shrink:0;color:var(--wp-text-40);transition:color .15s ease}.wp-header-address-btn:hover .wp-header-address-copy{color:var(--wp-text-60)}.wp-header-address-copy[data-copied=true]{color:var(--wp-mint)}.wp-top{flex-shrink:0;padding:0 16px 16px;display:flex;flex-direction:column;gap:16px}.wp-identity{display:flex;flex-direction:column;gap:8px}.wp-address{display:inline-flex;align-items:center;gap:8px}.wp-address-label{font-size:12px;font-weight:400;line-height:1.4;color:var(--wp-text-40)}.wp-address-chevron{display:inline-flex;align-items:center;color:var(--wp-text-40)}.wp-total{font-size:28px;font-weight:600;line-height:1.3;color:var(--wp-text);margin:0}.wp-asset{display:flex;flex-direction:column;gap:8px}.wp-asset-head{display:flex;align-items:center;gap:4px}.wp-asset-head-label{font-size:12px;font-weight:400;line-height:1.4;color:var(--wp-text-40)}.wp-asset-head-icon{display:inline-flex;color:var(--wp-text-40)}.wp-bar{display:flex;height:20px;border-radius:4px;overflow:hidden}.wp-bar-seg{display:flex;align-items:center;justify-content:center;min-width:8px}.wp-bar-seg-pct{font-size:10px;font-weight:400;line-height:1.3;color:#fff;white-space:nowrap}.wp-legend{display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap}.wp-legend-item{display:flex;align-items:center;gap:4px}.wp-legend-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.wp-legend-label{font-size:12px;font-weight:400;line-height:1.4;color:var(--wp-text-40)}.wp-tabs{flex-shrink:0;padding:8px 16px}.wp-tabs-inner{display:flex;align-items:center;padding:0 2px}.wp-tabs-list{display:flex;gap:20px;flex:1}.wp-tab{display:flex;flex-direction:column;align-items:center;justify-content:space-between;height:44px;padding-top:12px;background:transparent;border:none;cursor:pointer}.wp-tab-text{font-size:14px;font-weight:600;line-height:1.4;color:var(--wp-text-30);white-space:nowrap}.wp-tab[data-active=true] .wp-tab-text{color:var(--wp-mint)}.wp-tab-bar{width:20px;height:4px;border-radius:2px;background:var(--wp-mint);opacity:0}.wp-tab[data-active=true] .wp-tab-bar{opacity:1}.wp-tabs-filter-wrap{position:relative;display:flex;align-items:center;width:44px;height:44px;flex-shrink:0}.wp-tabs-filter-grad{position:absolute;inset:0;background:linear-gradient(to left,var(--wp-bg) 74%,transparent);pointer-events:none}.wp-tabs-filter{position:relative;display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;padding:0;border:none;background:transparent;cursor:pointer;color:var(--wp-text-40)}.wp-body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;scrollbar-width:thin;scrollbar-color:var(--wp-border) transparent}.wp-body::-webkit-scrollbar{width:4px}.wp-body::-webkit-scrollbar-track{background:transparent}.wp-body::-webkit-scrollbar-thumb{background:var(--wp-border);border-radius:4px}.wp-body-inner{padding:16px 16px 32px;display:flex;flex-direction:column;gap:32px}.wp-fullscreen .wp-body-inner{padding-top:8px;gap:24px}.wp-summary-card{display:flex;flex-direction:column;gap:8px;width:100%;padding:0;border-radius:0;background:transparent;box-shadow:none;box-sizing:border-box}.wp-summary-head{position:relative;display:flex;align-items:center;justify-content:flex-start;gap:4px}.wp-summary-label{font-size:14px;font-weight:400;line-height:1.5;letter-spacing:0;color:var(--wp-text-40);white-space:nowrap}.wp-address{display:inline-flex;align-items:center;gap:8px;min-width:0}.wp-address-avatar{width:16px;height:16px;border-radius:50%;flex-shrink:0;background:radial-gradient(circle at 68% 34%,#ff78d1 0 18%,transparent 19%),radial-gradient(circle at 34% 66%,#00d4aa 0 20%,transparent 21%),linear-gradient(135deg,#7346f3,#0488fc)}.wp-address-label{font-size:12px;font-weight:400;line-height:1.4;letter-spacing:0;color:var(--wp-text-40);white-space:nowrap}.wp-address-chevron{display:inline-flex;align-items:center;color:var(--wp-text-40);flex-shrink:0}.wp-summary-badge{display:inline-flex;align-items:center;justify-content:center;padding:4px 8px;border-radius:999px;background:#019d921a;color:var(--wp-mint);font-size:12px;font-weight:400;line-height:1.4;letter-spacing:0;white-space:nowrap}.wp-summary-estimate{position:relative;display:inline-flex;align-items:center;gap:6px;flex-shrink:0}.wp-estimate-info{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:0;border-radius:50%;background:transparent;color:var(--wp-text-40);cursor:help}.wp-estimate-tooltip{position:absolute;top:calc(100% + 8px);left:0;right:auto;z-index:3;display:flex;flex-direction:column;gap:8px;width:min(280px,calc(100vw - 48px));padding:12px;border-radius:8px;background:var(--wp-tooltip-bg);box-shadow:0 12px 32px #0000003d;color:var(--wp-tooltip-text);font-size:12px;font-weight:400;line-height:1.4;letter-spacing:0;text-align:left;white-space:normal;opacity:0;pointer-events:none;transform:translateY(-4px);visibility:hidden;transition:opacity .15s ease,transform .15s ease,visibility .15s ease}.wp-estimate-tooltip-title{display:block;font-size:12px;font-weight:600;line-height:1.3}.wp-estimate-tooltip-group{display:flex;flex-direction:column;gap:4px}.wp-estimate-tooltip-item{display:block;color:#ffffffc7}.wp-estimate-tooltip-item:before{content:\\\"- \\\"}.wp-estimate-info:hover .wp-estimate-tooltip,.wp-estimate-info:focus .wp-estimate-tooltip,.wp-estimate-info:focus-visible .wp-estimate-tooltip{opacity:1;transform:translateY(0);visibility:visible}.wp-total{font-family:var( --wp-total-font-family, Sora, Inter, system-ui, -apple-system, sans-serif );font-size:36px;font-weight:600;line-height:1.25;letter-spacing:0;color:var(--wp-text);margin:0}.wp-svc-card{display:flex;flex-direction:column;gap:12px;width:100%}.wp-svc-card-head{display:flex;align-items:center;gap:6px}.wp-svc-card-icon{width:24px;height:24px;border-radius:4px;flex-shrink:0;object-fit:cover}.wp-svc-card-title{font-size:16px;font-weight:600;line-height:1.5;letter-spacing:0;color:var(--wp-text-60);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wp-svc-items{display:flex;flex-direction:column;gap:8px;width:100%;padding:0;border-radius:0;background:transparent;box-shadow:none;box-sizing:border-box}.wp-item{position:relative;display:flex;flex-direction:column;gap:20px;width:100%;padding:12px;border-radius:12px;background:var(--wp-card);box-shadow:var(--wp-card-shadow, none);box-sizing:border-box;color:inherit;text-decoration:none;cursor:pointer;outline:none;transition:opacity .15s ease}.wp-item+.wp-item{padding-top:12px;border-top:0}.wp-item:hover{opacity:.88}.wp-item-body{display:flex;flex-direction:column;gap:12px;min-width:0}.wp-order-head{display:flex;align-items:center;width:100%}.wp-item-line{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;min-height:21px;min-width:0}.wp-item-label{flex-shrink:0;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:0;color:var(--wp-text-30);white-space:nowrap}.wp-line-value,.wp-order-side{min-width:0;overflow:hidden;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:0;color:var(--wp-text-60);text-align:right;text-overflow:ellipsis;white-space:nowrap}.wp-order-side--buy{color:var(--wp-mint)}.wp-order-side--sell{color:var(--wp-red)}.wp-svc-token{display:flex;align-items:center;justify-content:flex-end;gap:6px;min-width:0;min-height:21px}.wp-svc-token>span{font-size:14px;font-weight:500;line-height:1.5;letter-spacing:0;color:var(--wp-text-60);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wp-svc-token-icon{width:16px;height:16px;border-radius:50%;flex-shrink:0;object-fit:cover}.wp-item[data-side=reward] .wp-svc-token-icon{border-radius:0;object-fit:contain}.wp-svc-token--amount{align-items:flex-start}.wp-svc-token--amount>.wp-svc-token-icon{margin-top:2.5px}.wp-token-stack{display:inline-flex;align-items:center;justify-content:flex-end;width:29px;min-width:29px;overflow:visible}.wp-token-stack .wp-svc-token-icon+.wp-svc-token-icon{margin-left:-3.5px}.wp-token-subtle,.wp-item-usd{color:var(--wp-text-30)!important}.wp-svc-amount{display:flex;flex-direction:column;align-items:flex-end;justify-content:center;gap:1px;min-width:0}.wp-svc-amount-main{font-size:14px;font-weight:500;line-height:1.5;letter-spacing:0;color:var(--wp-text-60);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}.wp-svc-amount .wp-item-usd{font-size:12px;font-weight:500;line-height:1.35}.wp-order-token{display:flex;align-items:center;gap:8px;min-width:0}.wp-order-token .wp-svc-token-icon{width:32px;height:32px}.wp-order-token-meta{display:flex;min-width:0;flex-direction:column;justify-content:center}.wp-order-token-symbol{overflow:hidden;color:var(--wp-text-60);font-size:14px;font-weight:500;line-height:1.5;text-overflow:ellipsis;white-space:nowrap}.wp-order-token-pair{overflow:hidden;color:var(--wp-text-30);font-size:12px;font-weight:500;line-height:1.5;text-overflow:ellipsis;white-space:nowrap}.wp-item-tags{display:flex;align-items:stretch;gap:8px;width:100%}.wp-tag{appearance:none;display:inline-flex;flex:1 1 0;align-items:center;justify-content:center;gap:0;min-width:0;min-height:33px;padding:6px 10px;border-radius:6px;border:0;background:var(--wp-control);font-family:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:0;color:var(--wp-text-40);text-align:center;text-decoration:none;white-space:nowrap;box-sizing:border-box;transition:background-color .15s ease,border-color .15s ease,color .15s ease,transform .15s ease}a.wp-tag,button.wp-tag{cursor:pointer}.wp-tag:hover{background:var(--wp-control);transform:none}.wp-tag:focus-visible{outline:2px solid var(--wp-mint);outline-offset:2px}.wp-tag[aria-disabled=true],.wp-tag[data-disabled]{cursor:default;opacity:.48;pointer-events:none}.wp-tag-label{display:inline-flex;align-items:center}.wp-tag-icon{display:none}.wp-tag:hover .wp-tag-icon{transform:none}.wp-tag--mint{color:var(--wp-mint);background:var(--wp-accent-soft)}.wp-tag--mint:hover{background:var(--wp-accent-soft)}.wp-tag--muted{color:var(--wp-text-40)}.wp-body-inner[data-empty]{min-height:100%;box-sizing:border-box;justify-content:center}.wp-portfolio-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-height:280px;padding:32px 24px;text-align:center}.wp-portfolio-empty-icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;margin-bottom:6px;border-radius:50%;background:var(--wp-card);box-shadow:var(--wp-card-shadow, none);color:var(--wp-text-30)}.wp-portfolio-empty-title{margin:0;font-size:15px;font-weight:600;line-height:1.3;color:var(--wp-text-60)}.wp-portfolio-empty-desc{margin:0;font-size:13px;font-weight:400;line-height:1.4;color:var(--wp-text-30)}.wp-portfolio-loading{display:flex;align-items:center;justify-content:center;min-height:280px;padding:32px 24px}.wp-portfolio-spinner{width:22px;height:22px;border:2px solid var(--wp-border);border-top-color:var(--wp-mint);border-radius:50%;animation:wp-claim-spin .8s linear infinite}.wp-claim-overlay{position:absolute;inset:0;z-index:8;display:flex;align-items:flex-end;justify-content:center;background:#00000094;box-sizing:border-box}.wp-claim-sheet{display:flex;flex-direction:column;width:100%;max-height:calc(100% - 24px);overflow:hidden;border-radius:24px 24px 0 0;background:var(--wp-bg);color:var(--wp-text);box-shadow:0 -18px 48px #00000042;animation:wp-claim-slide-up .22s ease-out}@keyframes wp-claim-slide-up{0%{transform:translateY(18px);opacity:0}to{transform:translateY(0);opacity:1}}.wi-popover-content .wp-claim-sheet{min-height:min(600px,calc(100% - 24px))}.wi-popover-content .wp-claim-content{flex:1 1 auto}.wi-popover-content .wp-claim-actions{margin-top:auto}.wp-claim-grabber{display:flex;align-items:flex-start;justify-content:center;height:38px;min-height:38px;padding-top:12px;box-sizing:border-box}.wp-claim-grabber span{display:block;width:52px;height:5px;border-radius:999px;background:var(--wp-control)}.wp-claim-content{display:flex;flex-direction:column;align-items:center;gap:16px;width:100%;padding:20px;box-sizing:border-box}.wp-claim-icon{display:inline-flex;align-items:center;justify-content:center;width:64px;height:64px;border-radius:50%;flex-shrink:0}.wp-claim-icon--success{background:var(--wp-claim-success-soft);color:var(--wp-claim-success)}.wp-claim-icon--pending{background:#9b8cef29;color:var(--wp-claim-primary)}.wp-claim-icon--error{background:var(--wp-claim-error-soft);color:var(--wp-claim-error);font-size:30px;font-weight:600;line-height:1}.wp-claim-icon--token{background:#00d4aa29}.wp-claim-icon--token img{width:48px;height:48px;border-radius:50%;object-fit:cover}.wp-claim-icon--token span{display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:50%;background:var(--wp-mint);color:#101820;font-size:18px;font-weight:600}.wp-claim-spinner{width:36px;height:36px;border-radius:50%;border:3px solid rgba(155,140,239,.28);border-top-color:var(--wp-claim-primary);animation:wp-claim-spin .8s linear infinite}@keyframes wp-claim-spin{to{transform:rotate(360deg)}}.wp-claim-copy{display:flex;flex-direction:column;align-items:center;gap:16px;width:100%;text-align:center}.wp-claim-title{margin:0;font-size:20px;font-weight:600;line-height:1.3;letter-spacing:0;color:var(--wp-text)}.wp-claim-desc{max-width:335px;margin:0;font-size:13px;font-weight:400;line-height:1.3;letter-spacing:0;color:var(--wp-text-40)}.wp-claim-heading{display:flex;flex-direction:column;align-items:flex-start;gap:8px;width:100%}.wp-claim-error-message{margin:0;color:var(--wp-claim-error);font-size:13px;font-weight:400;line-height:1.3;letter-spacing:0}.wp-claim-reward-card{display:flex;align-items:center;justify-content:space-between;gap:6px;width:100%;min-height:60px;padding:16px 8px;border-radius:8px;background:var(--wp-card);box-sizing:border-box}.wp-claim-reward-token{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;flex-shrink:0;overflow:hidden;background:var(--wp-control)}.wp-claim-reward-token img{width:28px;height:28px;object-fit:cover}.wp-claim-reward-token span{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;background:var(--wp-mint);color:#101820;font-size:13px;font-weight:600;line-height:1}.wp-claim-reward-symbol{flex:1 1 auto;min-width:0;overflow:hidden;color:var(--wp-text);font-size:18px;font-weight:600;line-height:1.3;letter-spacing:0;text-overflow:ellipsis;white-space:nowrap}.wp-claim-reward-amount{flex-shrink:0;color:var(--wp-text);font-size:20px;font-weight:600;line-height:1.3;letter-spacing:0;white-space:nowrap}.wp-claim-reward-card[data-fit=sm] .wp-claim-reward-symbol{font-size:14px}.wp-claim-reward-card[data-fit=sm] .wp-claim-reward-amount{font-size:16px}.wp-claim-reward-card[data-fit=xs] .wp-claim-reward-symbol{font-size:12px}.wp-claim-reward-card[data-fit=xs] .wp-claim-reward-amount{font-size:13px}.wp-claim-account-card{display:flex;flex-direction:column;gap:12px;width:100%;padding:16px;border-radius:8px;background:var(--wp-card);box-sizing:border-box}.wp-claim-account-row{display:flex;align-items:center;justify-content:space-between;gap:4px;width:100%;min-height:40px}.wp-claim-account-row>span{flex-shrink:0;color:var(--wp-text-40);font-size:14px;font-weight:400;line-height:1.3;letter-spacing:0;white-space:nowrap}.wp-claim-account{display:flex;align-items:center;gap:8px;min-width:0}.wp-claim-account strong{overflow:hidden;color:var(--wp-text);font-size:15px;font-weight:600;line-height:1.6;letter-spacing:0;text-overflow:ellipsis;white-space:nowrap}.wp-claim-account-avatar{width:32px;height:32px;border-radius:50%;flex-shrink:0;background:radial-gradient(circle at 30% 28%,#d87aff 0 12%,transparent 13%),radial-gradient(circle at 68% 28%,#f0a1ff 0 12%,transparent 13%),radial-gradient(circle at 32% 70%,#b561e7 0 13%,transparent 14%),radial-gradient(circle at 70% 70%,#d4a2ff 0 13%,transparent 14%),radial-gradient(circle at 50% 50%,#8c4fc4 0 18%,transparent 19%),#4a315f;box-shadow:inset 0 0 0 3.37px var(--wp-control)}.wp-claim-divider{display:block;width:100%;height:1px;background:var(--wp-border)}.wp-claim-detail-list{display:flex;flex-direction:column;gap:8px;width:100%}.wp-claim-detail-row{display:flex;align-items:center;justify-content:space-between;gap:20px;width:100%;min-height:20px}.wp-claim-detail-row>span{min-width:0;color:var(--wp-text-40);font-size:13px;font-weight:400;line-height:1.3;letter-spacing:0;white-space:nowrap}.wp-claim-detail-row--sub>span:before{content:\\\"\\\";display:inline-block;width:3px;height:3px;margin:0 8px 3px;background:currentColor}.wp-claim-detail-row strong{min-width:0;overflow:hidden;color:var(--wp-text);font-size:13px;font-weight:400;line-height:1.3;letter-spacing:0;text-align:right;text-overflow:ellipsis;white-space:nowrap}.wp-claim-list{display:flex;flex-direction:column;gap:8px;width:100%;padding-bottom:8px}.wp-claim-list-item{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:56px;padding:8px 16px;border-radius:8px;background:var(--wp-card);box-sizing:border-box}.wp-claim-list-item>span{flex-shrink:0;font-size:14px;font-weight:400;line-height:1.3;color:var(--wp-text-40);white-space:nowrap}.wp-claim-list-item strong{display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;min-width:0;overflow:hidden;color:var(--wp-text);font-size:14px;font-weight:400;line-height:1.3;text-align:right;text-overflow:ellipsis;white-space:nowrap}.wp-claim-list-item img{width:16px;height:16px;border-radius:50%;flex-shrink:0}.wp-claim-hash{appearance:none;display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;min-width:0;padding:0;border:none;background:transparent;color:var(--wp-text);cursor:pointer;font:inherit}.wp-claim-hash svg{flex-shrink:0;color:var(--wp-text-60)}.wp-claim-gas-card{display:flex;flex-direction:column;gap:4px;width:100%;padding:14px 16px;border-radius:8px;border:1px solid var(--wp-border);background:var(--wp-card);box-sizing:border-box}.wp-claim-gas-row{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:28px}.wp-claim-gas-row>span{min-width:0;font-size:12px;font-weight:400;line-height:1.3;color:var(--wp-text-40)}.wp-claim-gas-row strong{min-width:0;overflow:hidden;color:var(--wp-text);font-size:12px;font-weight:400;line-height:1.3;text-align:right;text-overflow:ellipsis;white-space:nowrap}.wp-claim-gas-row--primary{min-height:32px}.wp-claim-gas-row--primary>span,.wp-claim-gas-row--primary strong{font-size:14px;color:var(--wp-text)}.wp-claim-fee-label{position:relative;display:inline-flex;align-items:center;text-decoration:underline dotted;text-underline-offset:3px;cursor:help;overflow:visible!important}.wp-claim-fee-tooltip{position:absolute;left:0;bottom:calc(100% + 8px);z-index:2;width:min(260px,calc(100vw - 64px));padding:10px 12px;border-radius:8px;background:var(--wp-tooltip-bg);color:var(--wp-tooltip-text);box-shadow:0 12px 32px #0000003d;font-size:11px;font-weight:400;line-height:1.4;text-align:left;text-decoration:none;white-space:normal;opacity:0;pointer-events:none;transform:translateY(4px);visibility:hidden;transition:opacity .15s ease,transform .15s ease,visibility .15s ease}.wp-claim-fee-label:hover .wp-claim-fee-tooltip,.wp-claim-fee-label:focus .wp-claim-fee-tooltip,.wp-claim-fee-label:focus-visible .wp-claim-fee-tooltip{opacity:1;transform:translateY(0);visibility:visible}.wp-claim-actions{display:flex;align-items:center;gap:8px;width:100%}.wp-claim-primary,.wp-claim-secondary{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:100%;min-width:0;height:52px;padding:0 24px;border:none;border-radius:40px;font-family:inherit;font-size:15px;font-weight:600;line-height:1.3;letter-spacing:0;white-space:nowrap;cursor:pointer}.wp-claim-primary{background:var(--wp-claim-primary);color:var(--wp-claim-primary-text)}.wp-claim-primary:disabled{cursor:default;opacity:.72}.wp-claim-secondary{background:var(--wp-claim-secondary-bg, var(--wp-control));color:var(--wp-claim-primary)}.wp-claim-actions[data-count=\\\"2\\\"] .wp-claim-primary{flex:1 1 0}.wp-claim-actions[data-count=\\\"2\\\"] .wp-claim-secondary{flex:0 0 120px}.wp-claim-button-spinner{width:16px;height:16px;border-radius:50%;border:2px solid rgba(32,37,48,.28);border-top-color:var(--wp-claim-primary-text);animation:wp-claim-spin .8s linear infinite}.wp-claim-home-indicator{display:flex;align-items:flex-end;justify-content:center;height:34px;min-height:34px;padding-bottom:8px;box-sizing:border-box}.wp-claim-home-indicator span{display:block;width:134px;height:5px;border-radius:16px;background:var(--wp-text-40)}@media(max-width:359px){.wp-header{gap:12px;padding:0 12px}.wp-total{font-size:24px}.wp-body-inner{padding:0 12px 128px;gap:20px}.wp-svc-items,.wp-claim-content{padding:18px 14px}.wp-item-line{gap:10px}.wp-svc-token>span{max-width:168px}}.wp-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.wi-drawer-content .wp-header,.wi-portfolio-body .wp-header{padding:16px}.wi-drawer-content .wp-header-edge--right .wp-header-btn,.wi-portfolio-body .wp-header-edge--right .wp-header-btn{top:16px;right:16px}.wp-withdraw-amount-card{display:flex;flex-direction:column;gap:12px;width:100%;padding:16px;border-radius:8px;background:var(--wp-card);box-sizing:border-box}.wp-withdraw-amount-row{display:flex;align-items:center;gap:8px;width:100%}.wp-withdraw-input{flex:1 1 auto;min-width:0;padding:0;border:none;background:transparent;color:var(--wp-text);font-family:inherit;font-size:24px;font-weight:600;line-height:1.3;letter-spacing:0;text-align:left;outline:none}.wp-withdraw-input::placeholder{color:var(--wp-text-30)}.wp-withdraw-input[aria-invalid=true]{color:var(--wp-claim-error)}.wp-withdraw-input-symbol{flex-shrink:0;color:var(--wp-text-40);font-size:15px;font-weight:600;line-height:1.3}.wp-withdraw-quick-row{display:flex;align-items:center;gap:8px;width:100%}.wp-withdraw-quick{appearance:none;flex:1 1 0;min-width:0;height:32px;padding:0 10px;border:1px solid var(--wp-border);border-radius:999px;background:transparent;color:var(--wp-text-60);font-family:inherit;font-size:13px;font-weight:600;line-height:1;white-space:nowrap;cursor:pointer}.wp-withdraw-quick:hover:not(:disabled){border-color:var(--wp-claim-primary);color:var(--wp-claim-primary)}.wp-withdraw-quick:disabled{cursor:default;opacity:.5}.wp-withdraw-content{overflow-y:auto}.wp-withdraw-token-list{display:flex;flex-direction:column;gap:8px;width:100%;max-height:216px;overflow-y:auto}.wp-withdraw-token{appearance:none;display:flex;align-items:center;gap:10px;width:100%;min-height:52px;padding:10px 12px;border:1px solid var(--wp-border);border-radius:8px;background:var(--wp-card);color:var(--wp-text);font-family:inherit;text-align:left;cursor:pointer;box-sizing:border-box}.wp-withdraw-token[data-selected]{border-color:var(--wp-claim-primary);box-shadow:inset 0 0 0 1px var(--wp-claim-primary)}.wp-withdraw-token:disabled{cursor:default;opacity:.6}.wp-withdraw-token-icon{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:50%;flex-shrink:0;overflow:hidden;background:var(--wp-control)}.wp-withdraw-token-icon img{width:28px;height:28px;object-fit:cover}.wp-withdraw-token-icon span{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;background:var(--wp-mint);color:#101820;font-size:13px;font-weight:600;line-height:1}.wp-withdraw-token-symbol{flex:1 1 auto;min-width:0;overflow:hidden;font-size:15px;font-weight:600;line-height:1.3;text-overflow:ellipsis;white-space:nowrap}.wp-withdraw-token-amount{flex-shrink:0;color:var(--wp-text-40);font-size:13px;font-weight:500;line-height:1.3;white-space:nowrap}\\n\")","\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport BigNumber from \"bignumber.js\";\nimport type { Environment } from \"../../../types\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport { parseNumeric } from \"../../../lib/numeric\";\nimport { cn } from \"../../../lib/utils\";\nimport { DEFAULT_ONEUSD_TERMS_URL } from \"../../../lib/constants\";\nimport { TrackingBoundary } from \"../../../lib/sentry\";\nimport { useSoftKeyboardOpen } from \"../../../hooks/use-soft-keyboard-open\";\nimport {\n  resolveEnvValue,\n  useRemoteConfigValue,\n  type AppsConfig,\n} from \"../../../lib/remote-config\";\nimport { useExitDelay } from \"../../../hooks/use-exit-delay\";\nimport { BackIcon, ChevronDownIcon, CloseIcon } from \"../../wallet-info/components/icons\";\nimport {\n  ArrowDownIcon,\n  ArrowRightIcon,\n  HistoryIcon,\n  SlidersIcon,\n} from \"./icons\";\nimport {\n  useBridgeFlow,\n  type BridgeFlow as BridgeFlowState,\n} from \"../model/useBridgeFlow\";\nimport type {\n  BridgeApprovalInfo,\n  BridgeApproveFn,\n  BridgeFailedInfo,\n  BridgeGetApprovalFn,\n  BridgeGetToTokensFn,\n  BridgeHistoryItem,\n  BridgeInfoRow,\n  BridgeInfoTokenRef,\n  BridgePathType,\n  BridgeQuoteFn,\n  BridgeSubmittedInfo,\n  BridgeSubmitFn,\n  BridgeToken,\n  BridgeTxSummary,\n} from \"../types\";\nimport \"../styles/bridge-flow.css\";\n\nexport interface BridgeFlowProps {\n  walletAddress: string;\n  tokens: BridgeToken[];\n  initialFromToken?: BridgeToken;\n  initialToToken?: BridgeToken;\n  history?: BridgeHistoryItem[];\n  initialSlippage?: string;\n  termsUrl?: string;\n  env?: Environment;\n  getQuote?: BridgeQuoteFn;\n  getToTokens?: BridgeGetToTokensFn;\n  getApproval?: BridgeGetApprovalFn;\n  approveBridge?: BridgeApproveFn;\n  submitBridge?: BridgeSubmitFn;\n  onSubmitted?: (info: BridgeSubmittedInfo) => void;\n  onSuccess?: (info: BridgeSubmittedInfo) => void;\n  onFailed?: (info: BridgeFailedInfo) => void;\n  onClose?: () => void;\n  onBackToWallet?: () => void;\n  /**\n   * 이 거래의 수수료를 서비스가 대납하는지. 견적(`BridgeTxSummary.txFeeInfo`)의\n   * `isDelegateFee` 는 가스만 가리키므로, 스왑/브릿지 수수료 대납은 호출부가\n   * 알려줘야 한다(Get ONEUSD 는 `pairs.swap/bridge.feeDelegated` 로 안다).\n   */\n  feeDelegated?: boolean;\n  /** Compact modal mode: fixed direction, no history entry, host-provided title. */\n  variant?: \"default\" | \"embedded\";\n  title?: string;\n  /** Embedded mode only. Defaults to whether `walletAddress` is present. */\n  isConnected?: boolean;\n  /** Opens the host wallet connection flow from the embedded CTA. */\n  onRequestConnect?: () => void;\n  className?: string;\n}\n\ntype HistoryFilter = \"all\" | \"completed\" | \"pending\";\n\nconst ThemeContext = React.createContext<string | undefined>(undefined);\n\nfunction useThemeAttr(): string | undefined {\n  return React.useContext(ThemeContext);\n}\n\nfunction useDetectTheme(ref: React.RefObject<HTMLElement | null>): string | undefined {\n  const [theme, setTheme] = React.useState<string | undefined>(undefined);\n  React.useEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    const read = () => {\n      const closest = el.closest(\"[data-theme]\");\n      setTheme(closest?.getAttribute(\"data-theme\") ?? undefined);\n    };\n    read();\n    const observer = new MutationObserver(read);\n    observer.observe(document.documentElement, { attributes: true, subtree: true, attributeFilter: [\"data-theme\"] });\n    return () => observer.disconnect();\n  }, [ref]);\n  return theme;\n}\n\nconst SLIPPAGE_PRESETS = [\n  { value: \"0.1\", label: \"0.1%\" },\n  { value: \"0.5\", label: \"0.5%\" },\n  { value: \"1\", label: \"1%\" },\n  { value: \"5\", label: \"5%\" },\n] as const;\n\nconst HISTORY_FILTERS: Array<{ value: HistoryFilter; label: string }> = [\n  { value: \"all\", label: \"ALL\" },\n  { value: \"completed\", label: \"Completed\" },\n  { value: \"pending\", label: \"Pending\" },\n];\n\nfunction formatSlippageLabel(value: string, effective?: string): string {\n  // In Auto mode show the resolved percent alongside the label (e.g. \"Auto 1%\"),\n  // matching crossd's chip.\n  if (value === \"auto\") return effective ? `Auto ${effective}%` : \"Auto\";\n  return value ? `${value}%` : \"1%\";\n}\n\nfunction sanitizeSlippageInput(value: string): string {\n  const cleaned = value.replace(/[^\\d.]/g, \"\");\n  const [integer = \"\", ...decimalParts] = cleaned.split(\".\");\n  const decimal = decimalParts.join(\"\");\n  if (decimalParts.length === 0) return integer;\n  return `${integer}.${decimal.slice(0, 2)}`;\n}\n\nfunction tokenKey(token: BridgeToken): string {\n  return `${token.chainId}:${token.address.toLowerCase()}`;\n}\n\nfunction formatBalance(value: string): string {\n  const n = Number(value);\n  if (!Number.isFinite(n)) return value || \"0\";\n  return n.toLocaleString(\"en-US\", {\n    maximumFractionDigits: n >= 1 ? 2 : 6,\n  });\n}\n\nfunction formatUsd(amount: string, token?: BridgeToken): string {\n  const n = Number(amount || \"0\");\n  const price = token?.priceUsd ?? 0;\n  const value = Number.isFinite(n) ? n * price : 0;\n  return `≈ $${value.toLocaleString(\"en-US\", {\n    minimumFractionDigits: 2,\n    maximumFractionDigits: 2,\n  })} USD`;\n}\n\n// Truncate (floor) a decimal string to at most `maxDp` fraction digits for\n// display, trimming trailing zeros. Display-only — never reused in computation.\nfunction clampDecimals(value: string, maxDp = 6): string {\n  if (!value) return value;\n  const [intPart, fracPart = \"\"] = value.split(\".\");\n  if (!fracPart) return intPart;\n  const truncated = fracPart.slice(0, maxDp).replace(/0+$/, \"\");\n  return truncated ? `${intPart}.${truncated}` : intPart;\n}\n\nfunction exceedsBalance(amount: string, token?: BridgeToken): boolean {\n  if (!token) return false;\n  const value = parseNumeric(amount);\n  const balance = parseNumeric(token.balance);\n  if (!value || !balance) return false;\n  return value.isGreaterThan(balance);\n}\n\n\n\nfunction formatRateDecimal(value: BigNumber): string {\n  if (!value.isFinite() || value.isNaN()) return \"0\";\n  const fixed = value.decimalPlaces(6, BigNumber.ROUND_DOWN).toFixed(6);\n  const trimmed = fixed.replace(/\\.?0+$/, \"\");\n  const [integer = \"0\", decimal] = trimmed.split(\".\");\n  const integerWithCommas = new BigNumber(integer || \"0\").toFormat(0);\n  return decimal ? `${integerWithCommas}.${decimal}` : integerWithCommas;\n}\n\nfunction shortHash(hash: string): string {\n  if (hash.length <= 14) return hash;\n  return `${hash.slice(0, 6)}...${hash.slice(-6)}`;\n}\n\nfunction formatHistoryTimestamp(timestamp: string): string {\n  const date = new Date(timestamp);\n  if (Number.isNaN(date.getTime())) return timestamp;\n\n  const pad = (value: number) => value.toString().padStart(2, \"0\");\n  return [\n    `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`,\n    `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`,\n  ].join(\" \");\n}\n\nfunction formatHistoryTokenAmount(value: string): string {\n  const amount = parseNumeric(value);\n  if (!amount) return value || \"0\";\n  return formatRateDecimal(amount);\n}\n\nfunction formatHistoryUsd(amount: string, token?: BridgeToken): string | null {\n  if (token?.priceUsd === undefined) return null;\n  const parsed = parseNumeric(amount);\n  if (!parsed) return null;\n  const value = parsed.multipliedBy(token.priceUsd);\n  if (!value.isFinite()) return null;\n  return `$${value.toFormat(2)}`;\n}\n\nfunction getHistoryStatus(\n  item: BridgeHistoryItem,\n): \"pending\" | \"completed\" | \"failed\" {\n  if (item.status === \"success\") return \"completed\";\n  if (item.status === \"failed\") return \"failed\";\n  return \"pending\";\n}\n\nfunction historyMatchesFilter(item: BridgeHistoryItem, filter: HistoryFilter): boolean {\n  if (filter === \"all\") return true;\n  return getHistoryStatus(item) === filter;\n}\n\nfunction historyStatusLabel(status: ReturnType<typeof getHistoryStatus>): string {\n  if (status === \"completed\") return \"Completed\";\n  if (status === \"failed\") return \"Failed\";\n  return \"Pending\";\n}\n\nfunction chainLabel(chainId: number): string {\n  if (chainId === 56 || chainId === 97) return \"BSC\";\n  if (chainId === 612044 || chainId === 612055 || chainId === 5555) return \"ONEchain\";\n  return `Chain ${chainId}`;\n}\n\nfunction sameBridgeAsset(a?: BridgeToken, b?: BridgeToken): boolean {\n  return (\n    !!a &&\n    !!b &&\n    a.chainId === b.chainId &&\n    a.address.toLowerCase() === b.address.toLowerCase()\n  );\n}\n\nfunction inferPathType(\n  from?: BridgeToken,\n  to?: BridgeToken,\n): BridgePathType | undefined {\n  if (!from || !to) return undefined;\n  if (from.chainId === to.chainId) return \"swap\";\n  if (\n    sameBridgeAsset(from, to) ||\n    from.symbol.toLowerCase() === to.symbol.toLowerCase()\n  ) {\n    return \"bridge\";\n  }\n  return \"swap-bridge\";\n}\n\nfunction isDirectSwapType(pathType?: BridgePathType): boolean {\n  return (\n    pathType === \"swap\" ||\n    pathType === \"deposit-swap\" ||\n    pathType === \"swap-withdraw\"\n  );\n}\n\nfunction isZeroAmount(value: string): boolean {\n  return !value || value === \"0\" || Number(value) === 0;\n}\n\nfunction getQuoteFieldErrors(message: string): {\n  from?: string;\n  to?: string;\n} {\n  const normalized = message.trim().toLowerCase();\n  if (!normalized) return {};\n\n  if (normalized.startsWith(\"minimum \")) {\n    return { from: message };\n  }\n\n  if (normalized.startsWith(\"available \") && normalized.includes(\"or more\")) {\n    return { to: message };\n  }\n\n  if (\n    normalized.includes(\"insufficient balance\") ||\n    normalized.includes(\"not enough balance\") ||\n    normalized.includes(\"exceeds balance\") ||\n    normalized.includes(\"insufficient network fee\")\n  ) {\n    return { from: message };\n  }\n\n  if (\n    normalized.includes(\"insufficient liquidity\") ||\n    normalized.includes(\"not enough liquidity\")\n  ) {\n    return { to: message };\n  }\n\n  return {};\n}\n\nfunction defaultSummary(flow: BridgeFlowState): BridgeTxSummary {\n  const from = flow.fromToken;\n  const to = flow.toToken;\n  const pathType = inferPathType(from, to);\n  const fromAmount = flow.fromAmount || \"0\";\n  const toAmount = flow.toAmount || \"0\";\n  const showBridge = pathType !== undefined && !isDirectSwapType(pathType);\n  const showSwap = pathType !== undefined && pathType !== \"bridge\";\n\n  return {\n    pathType,\n    bridgeInfo: showBridge ? [\n      {\n        label: \"Route\",\n        value: from && to ? `${chainLabel(from.chainId)} → ${chainLabel(to.chainId)}` : \"-\",\n      },\n      {\n        label: \"Validator Fee\",\n        value: isZeroAmount(fromAmount) ? \"0\" : \"Free\",\n        tone: isZeroAmount(fromAmount) ? \"default\" : \"accent\",\n      },\n      {\n        label: \"Network Fee\",\n        value: isZeroAmount(fromAmount) ? \"0\" : \"Free\",\n        tone: isZeroAmount(fromAmount) ? \"default\" : \"accent\",\n      },\n    ] : undefined,\n    swapInfo: showSwap ? [\n      from && to\n        ? {\n            label: \"Route\",\n            value: \"\",\n            routeTokens: [\n              { symbol: from.symbol, iconUrl: from.iconUrl },\n              { symbol: to.symbol, iconUrl: to.iconUrl },\n            ],\n          }\n        : { label: \"Route\", value: \"-\" },\n      { label: \"Price Impact\", value: \"0%\" },\n      {\n        label: \"In Amount\",\n        value: fromAmount,\n        valueToken: from ? { symbol: from.symbol, iconUrl: from.iconUrl } : undefined,\n      },\n      {\n        label: \"Swap Fee\",\n        value: \"0\",\n        valueToken: from ? { symbol: from.symbol, iconUrl: from.iconUrl } : undefined,\n      },\n      {\n        label: \"Out Amount\",\n        value: toAmount,\n        valueToken: to ? { symbol: to.symbol, iconUrl: to.iconUrl } : undefined,\n      },\n    ] : undefined,\n    txFee: [\n      { label: \"Priority Fee Per Gas\", value: \"0 Gwei\" },\n      { label: \"Max Fee Per Gas\", value: \"0 Gwei\" },\n      { label: \"Gas Limit\", value: \"0\" },\n    ],\n    txFeeInfo: {\n      estTxFee: \"0\",\n      isDelegateFee: false,\n    },\n  };\n}\n\nfunction TokenIcon({ token, size = 20 }: { token?: BridgeToken; size?: number }) {\n  const iconUrl = token?.iconUrl;\n  const hasImage = Boolean(iconUrl);\n  return (\n    <span\n      className=\"bf-token-icon\"\n      data-has-image={hasImage ? \"true\" : \"false\"}\n      style={{ width: size, height: size }}\n    >\n      {iconUrl ? (\n        <img src={iconUrl} alt=\"\" width={size} height={size} draggable={false} />\n      ) : (\n        <span className=\"bf-token-fallback\">{token?.symbol?.slice(0, 1) ?? \"?\"}</span>\n      )}\n    </span>\n  );\n}\n\n/**\n * 다음 토큰. 선택지가 사실상 둘뿐이라(브릿지 대상 쌍은 항상 2개) pill 클릭은\n * 목록을 펼치는 게 아니라 곧바로 상대 토큰으로 바꾼다 — 두 항목짜리 드롭다운은\n * 클릭을 한 번 더 요구하는 토글일 뿐이다. 셋 이상이 되어도 순환으로 동작한다.\n */\nfunction nextToken(\n  choices: BridgeToken[],\n  current?: BridgeToken,\n): BridgeToken | undefined {\n  if (choices.length === 0) return undefined;\n  const index = choices.findIndex((token) => sameBridgeAsset(token, current));\n  return choices[(index + 1) % choices.length];\n}\n\nfunction BridgeAmountPanel({\n  label,\n  token,\n  amount,\n  usd,\n  onAmountChange,\n  onTokenClick,\n  locked = false,\n}: {\n  label: \"From\" | \"To\";\n  token?: BridgeToken;\n  amount: string;\n  usd: string;\n  onAmountChange: (value: string) => void;\n  onTokenClick?: () => void;\n  // When the side has no real choice (e.g. a single available \"to\" token), the\n  // pill is fixed: hide the chevron and make it non-interactive.\n  locked?: boolean;\n}) {\n  return (\n    <section className=\"bf-amount-panel\">\n      <div className=\"bf-amount-head\">\n        <span>{label}</span>\n        <span className=\"bf-balance\">\n          {formatBalance(token?.balance ?? \"0\")} {token?.symbol ?? \"\"}\n        </span>\n      </div>\n      <div className=\"bf-amount-main\">\n        <label className=\"bf-amount-input-wrap\">\n          <span className=\"bf-sr-only\">{label} amount</span>\n          <input\n            type=\"text\"\n            inputMode=\"decimal\"\n            value={amount}\n            onChange={(event) => onAmountChange(event.target.value)}\n            placeholder=\"0\"\n            className=\"bf-amount-input\"\n            data-size={amountInputSize(amount)}\n          />\n          <span className=\"bf-amount-usd\">{usd}</span>\n        </label>\n        <button\n          type=\"button\"\n          className=\"bf-token-pill\"\n          onClick={locked ? undefined : onTokenClick}\n          disabled={!locked && !onTokenClick}\n          data-locked={locked ? \"true\" : undefined}\n          data-track={locked ? undefined : \"token_switch\"}\n          data-track-id={label.toLowerCase()}\n          aria-label={`Switch ${label.toLowerCase()} token`}\n        >\n          <span className=\"bf-token-pill-face\" key={token ? tokenKey(token) : \"none\"}>\n            <TokenIcon token={token} />\n            <span>{token?.symbol ?? \"Select\"}</span>\n          </span>\n          {!locked && <ChevronDownIcon size={16} />}\n        </button>\n      </div>\n    </section>\n  );\n}\n\nfunction formatEmbeddedAmount(value: string): string {\n  if (!value) return \"\";\n  const [integer = \"\", decimal] = value.replace(/,/g, \"\").split(\".\");\n  const formattedInteger = integer.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n  return decimal === undefined ? formattedInteger : `${formattedInteger}.${decimal}`;\n}\n\nfunction amountInputSize(value: string): \"large\" | \"medium\" | \"small\" {\n  const length = value.replace(/,/g, \"\").trim().length;\n  if (length > 26) return \"small\";\n  if (length > 18) return \"medium\";\n  return \"large\";\n}\n\nfunction WalletGlyph() {\n  return (\n    <svg\n      width=\"14\"\n      height=\"14\"\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M2.333 4.667A1.667 1.667 0 0 1 4 3h7.333A1.667 1.667 0 0 1 13 4.667v6.666A1.667 1.667 0 0 1 11.333 13H4a1.667 1.667 0 0 1-1.667-1.667V4.667Z\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.25\"\n      />\n      <path\n        d=\"M2.667 5.333h9.666c.737 0 1.334.597 1.334 1.334v2.666c0 .737-.597 1.334-1.334 1.334H9.667A2.667 2.667 0 0 1 7 8a2.667 2.667 0 0 1 2.667-2.667Z\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.25\"\n      />\n      <circle cx=\"10\" cy=\"8\" r=\".75\" fill=\"currentColor\" />\n    </svg>\n  );\n}\n\nfunction EmbeddedAmountPanel({\n  label,\n  token,\n  amount,\n  onAmountChange,\n  onTokenClick,\n  onMax,\n  locked,\n}: {\n  label: \"From\" | \"To\";\n  token?: BridgeToken;\n  amount: string;\n  onAmountChange: (value: string) => void;\n  onTokenClick?: () => void;\n  onMax?: () => void;\n  locked: boolean;\n}) {\n  const isFrom = label === \"From\";\n  return (\n    <section className=\"bf-embedded-amount-panel\">\n      <div className=\"bf-embedded-amount-head\">\n        <span className=\"bf-embedded-amount-label\">\n          <strong>{label}</strong>\n          {!isFrom && <em>estimated</em>}\n        </span>\n        {token && (\n          <span className=\"bf-embedded-balance\">\n            <WalletGlyph />\n            <span className=\"bf-embedded-balance-value\">\n              {formatBalance(token.balance)}\n            </span>\n            <span>{token.symbol}</span>\n            {onMax && (\n              <button\n                type=\"button\"\n                onClick={onMax}\n                aria-label=\"Use maximum available balance\"\n              >\n                MAX\n              </button>\n            )}\n          </span>\n        )}\n      </div>\n      <div className=\"bf-embedded-amount-main\">\n        <button\n          type=\"button\"\n          className=\"bf-embedded-token-pill\"\n          onClick={locked ? undefined : onTokenClick}\n          disabled={locked}\n          data-locked={locked ? \"true\" : undefined}\n          aria-label={`Switch ${label.toLowerCase()} token`}\n        >\n          <span className=\"bf-token-pill-face\" key={token ? tokenKey(token) : \"none\"}>\n            <TokenIcon token={token} />\n            <span>{token?.symbol ?? \"Select\"}</span>\n          </span>\n          {!locked && <ChevronDownIcon size={14} />}\n        </button>\n        <label className=\"bf-embedded-input-wrap\">\n          <span className=\"bf-sr-only\">{label} amount</span>\n          <input\n            type=\"text\"\n            inputMode=\"decimal\"\n            value={formatEmbeddedAmount(amount)}\n            onChange={(event) => onAmountChange(event.target.value)}\n            placeholder=\"0.00\"\n            className=\"bf-embedded-amount-input\"\n            data-size={amountInputSize(amount)}\n          />\n        </label>\n      </div>\n    </section>\n  );\n}\n\nfunction BridgeSlippageSettings({\n  flow,\n  onClose,\n}: {\n  flow: BridgeFlowState;\n  onClose: () => void;\n}) {\n  const titleId = React.useId();\n  const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n  const selectedPreset = SLIPPAGE_PRESETS.find(\n    (preset) => preset.value === flow.slippage,\n  );\n  const [customDraft, setCustomDraft] = React.useState(\n    flow.slippage && !selectedPreset ? flow.slippage : \"\",\n  );\n  const hasCustomDraft = customDraft.trim() !== \"\" && customDraft.trim() !== \".\";\n\n  const customValue = hasCustomDraft ? Number(customDraft) : NaN;\n  const exceedsMax = Number.isFinite(customValue) && customValue > 50;\n  const highSlippage =\n    Number.isFinite(customValue) && customValue > 5 && !exceedsMax;\n\n  const selectPreset = (value: string) => {\n    flow.setSlippage(value);\n    onClose();\n  };\n\n  const applyCustom = () => {\n    if (!hasCustomDraft || exceedsMax) return;\n    flow.setSlippage(sanitizeSlippageInput(customDraft));\n    onClose();\n  };\n\n  React.useEffect(() => {\n    setPortalTarget(document.body);\n  }, []);\n\n  React.useEffect(() => {\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === \"Escape\") onClose();\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [onClose]);\n\n  if (!portalTarget) return null;\n\n  const theme = useThemeAttr();\n\n  return createPortal(\n    <div\n      className=\"bf-slippage-overlay\"\n      data-theme={theme}\n      role=\"presentation\"\n      onMouseDown={(event) => {\n        if (event.target === event.currentTarget) onClose();\n      }}\n    >\n      <section\n        className=\"bf-slippage-dialog\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby={titleId}\n      >\n        <header className=\"bf-slippage-head\">\n          <h3 id={titleId}>Slippage Settings</h3>\n          <button\n            type=\"button\"\n            className=\"bf-slippage-close\"\n            onClick={onClose}\n            aria-label=\"Close slippage settings\"\n          >\n            <CloseIcon size={24} />\n          </button>\n        </header>\n\n        <div className=\"bf-slippage-body\">\n          <p className=\"bf-slippage-desc\">\n            The maximum price difference you are willing to accept before the\n            transaction is reverted. Higher slippage increases the chance of\n            execution but may result in a worse price.\n          </p>\n\n          <div className=\"bf-slippage-controls\">\n            <div className=\"bf-slippage-presets\">\n              {SLIPPAGE_PRESETS.map((preset) => (\n                <button\n                  key={preset.value}\n                  type=\"button\"\n                  className=\"bf-slippage-preset\"\n                  data-selected={\n                    !hasCustomDraft && flow.slippage === preset.value\n                      ? \"true\"\n                      : \"false\"\n                  }\n                  onClick={() => selectPreset(preset.value)}\n                >\n                  {preset.label}\n                </button>\n              ))}\n            </div>\n\n            <label\n              className=\"bf-slippage-custom\"\n              data-selected={hasCustomDraft ? \"true\" : \"false\"}\n            >\n              <span className=\"bf-sr-only\">Custom slippage percent</span>\n              <input\n                value={customDraft}\n                onChange={(event) => {\n                  setCustomDraft(sanitizeSlippageInput(event.target.value));\n                }}\n                onKeyDown={(event) => {\n                  if (event.key === \"Enter\") {\n                    event.preventDefault();\n                    applyCustom();\n                  }\n                }}\n                inputMode=\"decimal\"\n                placeholder=\"Enter custom slippage\"\n                autoComplete=\"off\"\n                spellCheck={false}\n              />\n              <span aria-hidden=\"true\">%</span>\n            </label>\n          </div>\n\n          {exceedsMax ? (\n            <p className=\"bf-slippage-message\" data-variant=\"error\">\n              Slippage cannot exceed 50%.\n            </p>\n          ) : highSlippage ? (\n            <p className=\"bf-slippage-message\" data-variant=\"warning\">\n              Your transaction may be frontrun due to high slippage.\n            </p>\n          ) : null}\n\n          {hasCustomDraft && (\n            <button\n              type=\"button\"\n              className=\"bf-slippage-apply\"\n              onClick={applyCustom}\n              disabled={exceedsMax}\n            >\n              Apply\n            </button>\n          )}\n        </div>\n      </section>\n    </div>,\n    portalTarget,\n  );\n}\n\nfunction BridgeTermsModal({\n  flow,\n  env,\n}: {\n  flow: BridgeFlowState;\n  env: Environment;\n}) {\n  const titleId = React.useId();\n  const [checked, setChecked] = React.useState(false);\n  const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n  const theme = useThemeAttr();\n  // 호출부 prop > apps.json(legal.termsUrl) > dapp-ui 공통 폴백 순.\n  const remoteTermsUrl = useRemoteConfigValue<AppsConfig, string>(\n    \"apps.json\",\n    env,\n    (config) => resolveEnvValue(config?.legal?.termsUrl, env),\n  );\n  const termsUrl = flow.termsUrl ?? remoteTermsUrl ?? DEFAULT_ONEUSD_TERMS_URL;\n\n  React.useEffect(() => {\n    setPortalTarget(document.body);\n  }, []);\n\n  React.useEffect(() => {\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === \"Escape\") flow.cancelTerms();\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [flow]);\n\n  const [visible, animState] = useExitDelay(flow.termsOpen);\n\n  if (!portalTarget || !visible) return null;\n\n  return createPortal(\n    <div\n      className=\"bf-action-modal-overlay\"\n      data-theme={theme}\n      data-state={animState}\n      role=\"presentation\"\n      onPointerDown={(event) => {\n        if (event.target === event.currentTarget) flow.cancelTerms();\n      }}\n    >\n      <section\n        className=\"bf-action-modal bf-terms-modal\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby={titleId}\n      >\n        <h3 id={titleId}>Agree to Terms of Service</h3>\n        <p className=\"bf-action-modal-desc\">\n          Please make sure to review the Terms of Service before using ONEUSD.\n        </p>\n\n        <div className=\"bf-terms-check-row\" data-checked={checked ? \"true\" : \"false\"}>\n          <button\n            type=\"button\"\n            className=\"bf-terms-check-button\"\n            role=\"checkbox\"\n            aria-checked={checked}\n            aria-label=\"Agree to Terms of Use\"\n            data-track=\"terms_check\"\n            onClick={() => setChecked((value) => !value)}\n          >\n            <span\n              className=\"bf-terms-checkbox\"\n              data-checked={checked ? \"true\" : \"false\"}\n              aria-hidden=\"true\"\n            />\n          </button>\n          <div className=\"bf-terms-copy\">\n            {/* Bigger hit target for the checkbox next to it. Keyboard/a11y stays\n                on the role=\"checkbox\" button, so this stays a plain span. */}\n            <span\n              className=\"bf-terms-label\"\n              onClick={() => setChecked((value) => !value)}\n            >\n              Agree to Terms of Use\n            </span>\n            {termsUrl && (\n              <a\n                href={termsUrl}\n                target=\"_blank\"\n                rel=\"noreferrer\"\n              >\n                Terms of Service\n              </a>\n            )}\n          </div>\n        </div>\n\n        <p className=\"bf-terms-privacy\">\n          I have read and acknowledged the Privacy Policy to use ONEUSD\n        </p>\n\n        <button\n          type=\"button\"\n          className=\"bf-action-primary\"\n          disabled={!checked || flow.status === \"submitting\"}\n          onClick={flow.confirmTerms}\n          data-track=\"terms_confirm\"\n        >\n          Confirm\n        </button>\n      </section>\n    </div>,\n    portalTarget,\n  );\n}\n\nfunction CopyButton({ value, disabled = false }: { value: string; disabled?: boolean }) {\n  const [copied, setCopied] = React.useState(false);\n  return (\n    <button\n      type=\"button\"\n      className=\"bf-copy-button\"\n      aria-label=\"Copy token address\"\n      disabled={disabled}\n      onClick={() => {\n        if (disabled) return;\n        void navigator.clipboard?.writeText(value).then(() => {\n          setCopied(true);\n          window.setTimeout(() => setCopied(false), 1200);\n        });\n      }}\n    >\n      {copied ? \"OK\" : \"Copy\"}\n    </button>\n  );\n}\n\nfunction BridgeApproveModal({\n  flow,\n  approval: openApproval,\n}: {\n  flow: BridgeFlowState;\n  /** Null closes the modal -- it stays mounted briefly to run the exit animation. */\n  approval: BridgeApprovalInfo | null;\n}) {\n  const titleId = React.useId();\n  const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n  const theme = useThemeAttr();\n\n  React.useEffect(() => {\n    setPortalTarget(document.body);\n  }, []);\n\n  React.useEffect(() => {\n    // Stays mounted while closed (for the exit animation), so Escape must only\n    // bind while there is an approval on screen.\n    if (!openApproval) return undefined;\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (flow.status === \"submitting\") return;\n      if (event.key === \"Escape\") flow.cancelApproval();\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [flow, openApproval]);\n\n  const [approval, animState] = useExitDelay(openApproval);\n\n  if (!portalTarget || !approval) return null;\n\n  const disabled = flow.status === \"submitting\" || !flow.approveBridge;\n  const isApproving = flow.status === \"submitting\";\n\n  return createPortal(\n    <div\n      className=\"bf-action-modal-overlay\"\n      data-theme={theme}\n      data-state={animState}\n      role=\"presentation\"\n    >\n      <section\n        className=\"bf-action-modal bf-approve-modal\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby={titleId}\n      >\n        <h3 id={titleId}>Approve {approval.token.symbol}</h3>\n        <TokenIcon token={approval.token} size={72} />\n\n        <div className=\"bf-approve-address-box\">\n          <span>Token Address</span>\n          <div>\n            <strong>{approval.tokenAddress}</strong>\n            <CopyButton value={approval.tokenAddress} disabled={isApproving} />\n          </div>\n        </div>\n\n        <div className=\"bf-action-buttons\">\n          <button\n            type=\"button\"\n            className=\"bf-action-secondary\"\n            onClick={flow.cancelApproval}\n            disabled={flow.status === \"submitting\"}\n          >\n            Cancel\n          </button>\n          <button\n            type=\"button\"\n            className=\"bf-action-primary\"\n            onClick={flow.confirmApproval}\n            disabled={disabled}\n            aria-busy={isApproving}\n            data-track=\"approve_confirm\"\n          >\n            {isApproving && <span className=\"bf-action-button-spinner\" aria-hidden=\"true\" />}\n            <span>{isApproving ? \"Approving...\" : \"Approve\"}</span>\n          </button>\n        </div>\n      </section>\n    </div>,\n    portalTarget,\n  );\n}\n\nfunction BridgeApprovalSuccessModal({ flow }: { flow: BridgeFlowState }) {\n  const titleId = React.useId();\n  const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n  const theme = useThemeAttr();\n\n  React.useEffect(() => {\n    setPortalTarget(document.body);\n  }, []);\n\n  React.useEffect(() => {\n    if (!flow.approvalSuccessOpen) return;\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === \"Escape\") flow.cancelApproval();\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [flow]);\n\n  const [visible, animState] = useExitDelay(flow.approvalSuccessOpen);\n\n  if (!portalTarget || !visible) return null;\n  const isContinuing = flow.status === \"submitting\";\n\n  return createPortal(\n    <div\n      className=\"bf-action-modal-overlay\"\n      data-theme={theme}\n      data-state={animState}\n      role=\"presentation\"\n    >\n      <section\n        className=\"bf-action-modal bf-approval-success-modal\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby={titleId}\n      >\n        <h3 id={titleId}>Approval Success</h3>\n        <span className=\"bf-success-mark\" aria-hidden=\"true\" />\n        <p className=\"bf-action-modal-desc\">\n          Token approval confirmed successfully.\n          <br />\n          Please continue to the next step.\n        </p>\n\n        <div className=\"bf-action-buttons\">\n          <button\n            type=\"button\"\n            className=\"bf-action-secondary\"\n            onClick={flow.cancelApproval}\n            disabled={isContinuing}\n          >\n            Cancel\n          </button>\n          <button\n            type=\"button\"\n            className=\"bf-action-primary\"\n            onClick={flow.continueAfterApproval}\n            disabled={isContinuing}\n            aria-busy={isContinuing}\n          >\n            {isContinuing && <span className=\"bf-action-button-spinner\" aria-hidden=\"true\" />}\n            <span>{isContinuing ? \"Continuing...\" : \"Continue\"}</span>\n          </button>\n        </div>\n      </section>\n    </div>,\n    portalTarget,\n  );\n}\n\nfunction ExchangeRate({\n  fromAmount,\n  toAmount,\n  fromToken,\n  toToken,\n}: {\n  fromAmount: string;\n  toAmount: string;\n  fromToken?: BridgeToken;\n  toToken?: BridgeToken;\n}) {\n  const exchangeRate = React.useMemo(() => {\n    const from = parseNumeric(fromAmount);\n    const to = parseNumeric(toAmount);\n    if (!from || !to || from.lte(0) || to.lte(0)) return null;\n    return to.dividedBy(from);\n  }, [fromAmount, toAmount]);\n\n  // Same token across different chains (e.g. CROSS on BNB <-> CROSS on Cross)\n  // always bridges 1:1, so the \"1 CROSS = 1 CROSS\" rate adds no information.\n  const visible = Boolean(\n    exchangeRate && fromToken && toToken && fromToken.symbol !== toToken.symbol,\n  );\n\n  return (\n    <Collapse open={visible}>\n      <div className=\"bf-exchange-rate\">\n        <span>1 {fromToken?.symbol}</span>\n        <span aria-hidden=\"true\">=</span>\n        <span>\n          {exchangeRate ? formatRateDecimal(exchangeRate) : \"\"} {toToken?.symbol}\n        </span>\n      </div>\n    </Collapse>\n  );\n}\n\nfunction BridgeStatusToast({ flow }: { flow: BridgeFlowState }) {\n  if (!flow.submitToast) return null;\n  return (\n    <div\n      className=\"bf-status-toast\"\n      data-variant=\"processing\"\n      role=\"status\"\n      aria-live=\"polite\"\n    >\n      <span className=\"bf-status-toast-icon\" aria-hidden=\"true\">\n        <span className=\"bf-status-spinner\" />\n      </span>\n      <span className=\"bf-status-toast-copy\">\n        <strong>Transaction Processing</strong>\n        <span>Your transaction is being processed on the blockchain</span>\n      </span>\n    </div>\n  );\n}\n\nfunction BridgeErrorToast({ message }: { message: string }) {\n  const [visible, setVisible] = React.useState(false);\n\n  React.useEffect(() => {\n    if (!message) {\n      setVisible(false);\n      return undefined;\n    }\n\n    setVisible(true);\n    const timer = window.setTimeout(() => {\n      setVisible(false);\n    }, 3500);\n\n    return () => window.clearTimeout(timer);\n  }, [message]);\n\n  if (!message || !visible) return null;\n\n  return (\n    <div\n      className=\"bf-status-toast\"\n      data-variant=\"error\"\n      role=\"alert\"\n      aria-live=\"assertive\"\n    >\n      <span className=\"bf-status-toast-icon\" aria-hidden=\"true\">\n        <span className=\"bf-status-error-mark\">!</span>\n      </span>\n      <span className=\"bf-status-toast-copy\">\n        <strong>Error</strong>\n        <span>{message}</span>\n      </span>\n    </div>\n  );\n}\n\nfunction successTitle(pathType?: BridgePathType): string {\n  switch (pathType) {\n    case \"swap\":\n    case \"deposit-swap\":\n    case \"swap-withdraw\":\n      return \"Swap Transaction Sent\";\n    case \"bridge\":\n      return \"Bridge Transaction Sent\";\n    case \"swap-bridge\":\n      return \"Swap & Bridge Transaction Sent\";\n    case \"bridge-swap\":\n      return \"Bridge & Swap Transaction Sent\";\n    default:\n      return \"Transaction Sent\";\n  }\n}\n\nfunction BridgeSuccessModal({ flow }: { flow: BridgeFlowState }) {\n  const titleId = React.useId();\n  const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n  const submitted = flow.submitted;\n  const theme = useThemeAttr();\n\n  React.useEffect(() => {\n    setPortalTarget(document.body);\n  }, []);\n\n  React.useEffect(() => {\n    if (!flow.successDialogOpen) return;\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (event.key === \"Escape\") flow.dismissSuccessDialog();\n    };\n    document.addEventListener(\"keydown\", handleKeyDown);\n    return () => document.removeEventListener(\"keydown\", handleKeyDown);\n  }, [flow]);\n\n  if (!portalTarget || !flow.successDialogOpen || !submitted) return null;\n\n  return createPortal(\n    <div className=\"bf-success-overlay\" data-theme={theme} role=\"presentation\">\n      <section\n        className=\"bf-success-modal\"\n        role=\"dialog\"\n        aria-modal=\"true\"\n        aria-labelledby={titleId}\n      >\n        <div className=\"bf-success-body\">\n          <span className=\"bf-success-mark\" aria-hidden=\"true\" />\n          <div className=\"bf-success-copy\">\n            <strong id={titleId}>{successTitle(submitted.pathType)}</strong>\n            <span>Please check the results in the history.</span>\n          </div>\n\n          <div className=\"bf-success-route\" aria-hidden=\"true\">\n            {(submitted.routeTokens && submitted.routeTokens.length > 0\n              ? submitted.routeTokens\n              : [\n                  { symbol: submitted.fromToken.symbol, iconUrl: submitted.fromToken.iconUrl },\n                  { symbol: submitted.toToken.symbol, iconUrl: submitted.toToken.iconUrl },\n                ]\n            ).map((token, index) => (\n              <React.Fragment key={`${token.symbol}-${index}`}>\n                {index > 0 && <span className=\"bf-success-route-arrow\">→</span>}\n                <InfoTokenGlyph token={token} size={43} />\n              </React.Fragment>\n            ))}\n          </div>\n\n          <button\n            type=\"button\"\n            className=\"bf-success-ok\"\n            onClick={flow.dismissSuccessDialog}\n            data-track=\"success_ok\"\n          >\n            OK\n          </button>\n        </div>\n      </section>\n    </div>,\n    portalTarget,\n  );\n}\n\nfunction InfoTokenGlyph({\n  token,\n  size = 16,\n}: {\n  token: BridgeInfoTokenRef;\n  size?: number;\n}) {\n  const hasImage = Boolean(token.iconUrl);\n  return (\n    <span\n      className=\"bf-token-icon\"\n      data-has-image={hasImage ? \"true\" : \"false\"}\n      style={{ width: size, height: size }}\n    >\n      {token.iconUrl ? (\n        <img src={token.iconUrl} alt=\"\" width={size} height={size} draggable={false} />\n      ) : (\n        <span className=\"bf-token-fallback\">{token.symbol?.slice(0, 1) ?? \"?\"}</span>\n      )}\n    </span>\n  );\n}\n\nfunction InfoRowValue({ row }: { row: BridgeInfoRow }) {\n  if (row.routeTokens && row.routeTokens.length > 0) {\n    return (\n      <span className=\"bf-info-route\">\n        {row.routeTokens.map((token, index) => (\n          <React.Fragment key={`${token.symbol}-${index}`}>\n            {index > 0 && (\n              <span className=\"bf-info-route-arrow\" aria-hidden=\"true\">\n                →\n              </span>\n            )}\n            <InfoTokenGlyph token={token} />\n          </React.Fragment>\n        ))}\n      </span>\n    );\n  }\n  if (row.valueToken) {\n    return (\n      <span className=\"bf-info-value-token\">\n        <InfoTokenGlyph token={row.valueToken} />\n        <span>{row.value}</span>\n      </span>\n    );\n  }\n  return <>{row.value}</>;\n}\n\nfunction InfoRows({ rows }: { rows?: BridgeInfoRow[] }) {\n  if (!rows || rows.length === 0) return null;\n  return (\n    <div className=\"bf-info-rows\">\n      {rows.map((row) => (\n        <div className=\"bf-info-row\" key={`${row.label}-${row.value}`}>\n          <span className=\"bf-info-label\">{row.label}</span>\n          <span className=\"bf-info-value\" data-tone={row.tone ?? \"default\"}>\n            <InfoRowValue row={row} />\n          </span>\n        </div>\n      ))}\n    </div>\n  );\n}\n\nfunction getInfoTitle(\n  summary: BridgeTxSummary,\n  hasBridgeInfo: boolean,\n  hasSwapInfo: boolean,\n): string {\n  if (summary.pathType === \"bridge\") return \"Bridge Info\";\n  if (isDirectSwapType(summary.pathType)) return \"Swap Info\";\n  if (hasBridgeInfo && hasSwapInfo) return \"Swap & Bridge Info\";\n  if (hasBridgeInfo) return \"Bridge Info\";\n  return \"Swap Info\";\n}\n\n/**\n * 금액 입력에 따라 붙었다 사라지는 하단 info 블록의 높이 전환.\n *\n * 닫힐 때도 내용이 남아 있어야 접히는 게 보이므로, `open` 이 꺼지면 마지막으로\n * 열려 있던 children 을 그대로 붙잡고 접는다. 높이는 grid `0fr → 1fr` —\n * `height: auto` 를 트랜지션할 수 있는 가장 무난한 방법이고, 내용 높이를 미리\n * 재거나 JS 타이머를 둘 필요가 없다.\n */\nfunction Collapse({\n  open,\n  className,\n  children,\n}: {\n  open: boolean;\n  className?: string;\n  children: React.ReactNode;\n}) {\n  const lastOpenChildren = React.useRef<React.ReactNode>(null);\n  if (open) lastOpenChildren.current = children;\n  return (\n    <div\n      className={className ? `bf-collapse ${className}` : \"bf-collapse\"}\n      data-open={open ? \"true\" : \"false\"}\n      aria-hidden={open ? undefined : true}\n    >\n      <div className=\"bf-collapse-inner\">\n        {open ? children : lastOpenChildren.current}\n      </div>\n    </div>\n  );\n}\n\nfunction BridgeDetails({\n  summary,\n  from,\n  to,\n  feeDelegated,\n}: {\n  summary: BridgeTxSummary;\n  from?: RouteEndpoint;\n  to?: RouteEndpoint;\n  feeDelegated?: boolean;\n}) {\n  const hasBridgeInfo = Boolean(summary.bridgeInfo?.length);\n  const hasSwapInfo = Boolean(summary.swapInfo?.length);\n  const hasTxFee = Boolean(summary.txFee?.length);\n  const infoTitle = getInfoTitle(summary, hasBridgeInfo, hasSwapInfo);\n  const [infoOpen, setInfoOpen] = React.useState(true);\n  const [txFeeOpen, setTxFeeOpen] = React.useState(true);\n\n  return (\n    <div className=\"bf-details\">\n      {summary.liquidity && (\n        <section className=\"bf-liquidity\">\n          <div className=\"bf-liquidity-head\">\n            <span>{summary.liquidity.label ?? \"Available Liquidity\"}</span>\n            <span className=\"bf-liquidity-status\" data-status={summary.liquidity.status}>\n              {summary.liquidity.status ?? \"Normal\"}\n            </span>\n          </div>\n          <div className=\"bf-liquidity-track\">\n            <span style={{ width: `${summary.liquidity.percentage ?? 50}%` }} />\n          </div>\n        </section>\n      )}\n      {(hasBridgeInfo || hasSwapInfo) && (\n        <section\n          className=\"bf-detail-section bf-info-card\"\n          data-open={infoOpen ? \"true\" : \"false\"}\n        >\n          <button\n            type=\"button\"\n            className=\"bf-collapse-head\"\n            aria-expanded={infoOpen}\n            onClick={() => setInfoOpen((open) => !open)}\n            data-track=\"details_toggle\"\n          >\n            <h3>{infoTitle}</h3>\n            <ChevronDownIcon size={16} className=\"bf-collapse-chevron\" />\n          </button>\n          {infoOpen && (\n            <div className=\"bf-collapse-body\">\n              <RouteStepSections\n                summary={summary}\n                from={from}\n                to={to}\n                feeDelegated={feeDelegated}\n              />\n            </div>\n          )}\n        </section>\n      )}\n      {hasTxFee && (\n        <section\n          className=\"bf-detail-section bf-tx-fee-section\"\n          data-open={txFeeOpen ? \"true\" : \"false\"}\n        >\n          <button\n            type=\"button\"\n            className=\"bf-collapse-head bf-tx-fee-head\"\n            aria-expanded={txFeeOpen}\n            onClick={() => setTxFeeOpen((open) => !open)}\n            data-track=\"details_toggle\"\n          >\n            <h3>Est. Tx Fee</h3>\n            <div className=\"bf-tx-fee-total\">\n              {summary.txFeeInfo?.isDelegateFee && (\n                <span className=\"bf-fee-delegated\">Fee Delegated</span>\n              )}\n              {summary.txFeeInfo?.tokenIconUrl && (\n                <img\n                  className=\"bf-tx-fee-token\"\n                  src={summary.txFeeInfo.tokenIconUrl}\n                  alt={summary.txFeeInfo.tokenSymbol ?? \"\"}\n                  width={16}\n                  height={16}\n                  draggable={false}\n                />\n              )}\n              <span\n                data-delegated={\n                  summary.txFeeInfo?.isDelegateFee &&\n                  Number(summary.txFeeInfo?.estTxFee) > 0\n                    ? \"true\"\n                    : undefined\n                }\n              >\n                {clampDecimals(summary.txFeeInfo?.estTxFee ?? \"0\")}\n              </span>\n              <ChevronDownIcon size={16} className=\"bf-collapse-chevron\" />\n            </div>\n          </button>\n          {txFeeOpen && (\n            <div className=\"bf-collapse-body\">\n              <InfoRows rows={summary.txFee} />\n            </div>\n          )}\n        </section>\n      )}\n    </div>\n  );\n}\n\nfunction normalizeEmbeddedSummary(summary: BridgeTxSummary): BridgeTxSummary {\n  return {\n    ...summary,\n    bridgeInfo: summary.bridgeInfo?.map((row) => ({\n      ...row,\n      value:\n        row.label === \"Route\"\n          ? row.value.replace(/\\bCROSS\\b/g, \"ONEchain\")\n          : row.value,\n    })),\n    swapInfo: summary.swapInfo?.map((row) => ({\n      ...row,\n      label: row.label.startsWith(\"Swap Fee\") ? \"Est. Swap Fee\" : row.label,\n    })),\n  };\n}\n\n/** 입력 금액을 스텝 표기용으로 정리한다(콤마 + 6dp, 값이 없으면 undefined). */\nfunction formatAmountForStep(value?: unknown): string | undefined {\n  const amount = parseNumeric(value);\n  if (!amount || amount.lte(0)) return undefined;\n  return formatRateDecimal(amount);\n}\n\n/**\n * 환율 표시 문자열. 소수 앞자리 0이 많아 읽기 어려운 방향은 뒤집는다 —\n * `1 tONE = 0.000733 tONEUSD` 보다 `1 tONEUSD = 1,362.73 tONE` 이 읽힌다.\n * 값 자체는 같고 표시 방향만 바뀐다.\n */\nfunction formatRateLine(\n  fromAmount: string,\n  toAmount: string,\n  fromToken?: BridgeToken,\n  toToken?: BridgeToken,\n): string | undefined {\n  if (!fromToken || !toToken) return undefined;\n  const from = parseNumeric(fromAmount);\n  const to = parseNumeric(toAmount);\n  if (!from || !to || from.lte(0) || to.lte(0)) return undefined;\n\n  const forward = to.dividedBy(from);\n  if (forward.gte(\"0.001\")) {\n    return `1 ${fromToken.symbol} = ${formatRateDecimal(forward)} ${toToken.symbol}`;\n  }\n  const reversed = from.dividedBy(to);\n  return `1 ${toToken.symbol} = ${formatRateDecimal(reversed)} ${fromToken.symbol}`;\n}\n\n/**\n * Info 영역을 평면 5줄 목록이 아니라 **거래 단계**로 그린다(시안 C). 각 단계는\n * \"무엇이 얼마\" 한 줄이고, 그 단계에서 발생하는 값(수수료·가격 영향·환율)은\n * 그 아래 메타로 붙어 맥락을 갖는다. swap+bridge 복합 경로에서는 구간별로\n * 단계가 이어진다.\n */\ntype InfoTone = NonNullable<BridgeInfoRow[\"tone\"]>;\n\ninterface RouteStepMeta {\n  text: string;\n  tone?: InfoTone;\n}\n\ninterface RouteStep {\n  key: string;\n  title: string;\n  amount: string;\n  amountToken?: BridgeInfoTokenRef;\n  /** 구간 단계의 경로 표기 — 아이콘 배열로 오는 Route 행을 그대로 그린다. */\n  routeTokens?: BridgeInfoTokenRef[];\n  meta: RouteStepMeta[];\n  tags: string[];\n}\n\n/**\n * Price impact 위험도 구간. DEX 관행(경고 1%, 위험 5%)을 따른다 — 1% 미만은\n * 정상 거래라 색을 쓰지 않고, 5% 이상은 사용자가 손실을 인지해야 하는 값이다.\n */\nconst PRICE_IMPACT_WARNING = 1;\nconst PRICE_IMPACT_DANGER = 5;\n\n/** \"12.5%\" / \"0.000012\" 같은 표기에서 퍼센트 숫자만 뽑는다. 숫자가 아니면 null. */\nfunction parsePercent(value?: unknown): number | null {\n  return parseNumeric(value)?.toNumber() ?? null;\n}\n\nfunction priceImpactTone(value?: string): InfoTone | undefined {\n  const percent = parsePercent(value);\n  if (percent === null) return undefined;\n  if (percent >= PRICE_IMPACT_DANGER) return \"danger\";\n  if (percent >= PRICE_IMPACT_WARNING) return \"warning\";\n  return undefined;\n}\n\nconst IN_AMOUNT = /\\bin\\b.*amount|amount.*\\bin\\b|^from\\b/i;\nconst OUT_AMOUNT = /\\bout\\b.*amount|amount.*\\bout\\b|^to\\b|receive/i;\nconst FEE_ROW = /fee/i;\nconst CONTEXT_ROW = /impact|rate|slippage|minimum|min\\.|received/i;\nconst IMPACT_ROW = /impact/i;\nconst ROUTE_ROW = /^route$/i;\n\ninterface RouteSection {\n  key: string;\n  label: string;\n  routeText?: string;\n  routeTokens?: BridgeInfoTokenRef[];\n  inRow?: BridgeInfoRow;\n  outRow?: BridgeInfoRow;\n  meta: RouteStepMeta[];\n  rest: BridgeInfoRow[];\n}\n\n/**\n * 한 구간(swap 또는 bridge)의 행들을 분류한다. 구간마다 담고 있는 것이 다르다 —\n * swapInfo 는 In/Out Amount + Price Impact + Swap Fee 를, bridgeInfo 는 Route +\n * Validator/Network Fee 만 준다(금액 행이 없다). 그래서 금액은 있으면 쓰고,\n * 없으면 상위(from/to 입력값)에서 채운다.\n */\nfunction toRouteSection(\n  rows: BridgeInfoRow[],\n  key: string,\n  label: string,\n): RouteSection {\n  const inRow = rows.find((row) => IN_AMOUNT.test(row.label));\n  const outRow = rows.find((row) => OUT_AMOUNT.test(row.label));\n  const used = new Set<BridgeInfoRow>();\n  if (inRow) used.add(inRow);\n  if (outRow) used.add(outRow);\n\n  // Route 행은 어느 체인을 거치는지를 담고 있어 버리지 않는다. 아이콘 배열\n  // (routeTokens) 형태는 문자열로 못 합치므로 아래 목록에 그대로 남긴다.\n  const routeRow = rows.find(\n    (row) => ROUTE_ROW.test(row.label) || (row.routeTokens?.length ?? 0) > 0,\n  );\n  const routeTokens = routeRow?.routeTokens?.length\n    ? routeRow.routeTokens\n    : undefined;\n  const routeText = routeRow && !routeTokens ? routeRow.value : undefined;\n  if (routeRow) used.add(routeRow);\n\n  const meta: RouteStepMeta[] = [];\n  for (const row of rows) {\n    if (used.has(row)) continue;\n    const isFee = FEE_ROW.test(row.label);\n    if (!isFee && !CONTEXT_ROW.test(row.label)) continue;\n    used.add(row);\n    // 호출부가 값을 \"0\" 으로 주기도 하고 \"Free\" 로 주기도 한다.\n    const numeric = parsePercent(row.value);\n    const isFree =\n      isFee && (numeric === 0 || /^\\s*free\\s*$/i.test(String(row.value ?? \"\")));\n    // swap 구간의 무료 수수료는 아예 적지 않는다 — \"Est. Swap Fee Free\" 는\n    // 읽는 사람에게 아무 정보도 주지 않는다. bridge 구간은 무료가 곧 혜택이라\n    // (기존 브릿지 UI 도 오렌지 Free 로 강조했다) 그대로 남긴다.\n    if (isFree && key === \"swap\") continue;\n    meta.push({\n      text: isFree ? `${row.label} Free` : `${row.label} ${row.value}`,\n      tone: isFree\n        ? \"accent\"\n        : // Price impact는 값에 따라 색으로 위험도를 표시한다.\n          IMPACT_ROW.test(row.label)\n          ? row.tone === \"danger\" || row.tone === \"warning\"\n            ? row.tone\n            : priceImpactTone(row.value)\n          : undefined,\n    });\n  }\n\n  return {\n    key,\n    label,\n    routeText,\n    routeTokens,\n    inRow,\n    outRow,\n    meta,\n    rest: rows.filter((row) => !used.has(row)),\n  };\n}\n\nfunction RouteStepList({ steps }: { steps: RouteStep[] }) {\n  return (\n    <div className=\"bf-route-steps\">\n      {steps.map((step, index) => (\n        <div\n          className=\"bf-route-step\"\n          key={step.key}\n          data-last={index === steps.length - 1 ? \"true\" : \"false\"}\n          data-kind={step.key.startsWith(\"hop-\") ? \"hop\" : \"endpoint\"}\n        >\n          <span className=\"bf-route-rail\" aria-hidden=\"true\">\n            <span className=\"bf-route-line\" />\n            <span\n              className=\"bf-route-dot\"\n              data-final={index === steps.length - 1 ? \"true\" : \"false\"}\n            />\n          </span>\n          <span className=\"bf-route-body\">\n            <span className=\"bf-route-head\">\n              <span className=\"bf-route-title\">{step.title}</span>\n              <span className=\"bf-route-amount\">\n                {step.routeTokens?.length ? (\n                  step.routeTokens.map((token, tokenIndex) => (\n                    <React.Fragment key={`${token.symbol}-${tokenIndex}`}>\n                      {tokenIndex > 0 && (\n                        <span className=\"bf-info-route-arrow\" aria-hidden=\"true\">\n                          →\n                        </span>\n                      )}\n                      <InfoTokenGlyph token={token} />\n                    </React.Fragment>\n                  ))\n                ) : (\n                  <>\n                    {step.amountToken && (\n                      <InfoTokenGlyph token={step.amountToken} />\n                    )}\n                    {/* key: 값이 바뀌면 새로 마운트돼서 `bf-value-in` 이 다시\n                        재생된다 — 아래 info 행들이 쓰는 것과 같은 방식. */}\n                    <span className=\"bf-route-amount-value\" key={step.amount}>\n                      {step.amount}\n                    </span>\n                  </>\n                )}\n              </span>\n            </span>\n            {(step.meta.length > 0 || step.tags.length > 0) && (\n              <span className=\"bf-route-meta\">\n                {step.meta.map((item) => (\n                  <span key={item.text} data-tone={item.tone ?? \"default\"}>\n                    {item.text}\n                  </span>\n                ))}\n                {step.tags.map((tag) => (\n                  <span className=\"bf-route-tag\" key={tag}>\n                    {tag}\n                  </span>\n                ))}\n              </span>\n            )}\n          </span>\n        </div>\n      ))}\n    </div>\n  );\n}\n\n/** Price impact 행에 위험도 tone을 채운다(호출부가 이미 줬으면 그대로 둔다). */\nfunction withImpactTone(row: BridgeInfoRow): BridgeInfoRow {\n  if (!IMPACT_ROW.test(row.label)) return row;\n  if (row.tone === \"danger\" || row.tone === \"warning\") return row;\n  const tone = priceImpactTone(row.value);\n  return tone ? { ...row, tone } : row;\n}\n\n\nexport interface RouteEndpoint {\n  symbol?: string;\n  iconUrl?: string;\n  amount?: string;\n  /** 이 금액이 나가는/들어오는 체인 이름. 첫 단계가 어느 체인에서 시작하는지는\n   *  라우트 행이 아니라 여기서만 알 수 있다(브릿지는 체인을 넘는 거래다). */\n  chain?: string;\n}\n\n/**\n * 라우트 스텝 + 남은 행. Swap/Bridge 모달(기본)과 Get ONEUSD(embedded)가 같은\n * 표현을 쓰도록 여기 한 곳에서 만든다.\n *\n * 스텝 구성: `Send` → (구간마다 한 단계) → `Receive`. 구간이 하나뿐인 단순\n * 거래에서는 중간 단계를 넣지 않고 그 구간의 값들을 Send/Receive 메타로 붙인다 —\n * 한 줄짜리 \"Swap\" 단계를 끼워 넣어야 할 이유가 없다. 브릿지가 섞인 복합 경로는\n * 구간별 단계가 있어야 어디서 무엇이 일어나는지 읽힌다.\n */\nfunction RouteStepSections({\n  summary,\n  rateLine,\n  from,\n  to,\n  feeDelegated: feeDelegatedProp,\n  includeTxFee = false,\n}: {\n  summary: BridgeTxSummary;\n  rateLine?: string;\n  from?: RouteEndpoint;\n  to?: RouteEndpoint;\n  /** 스왑/브릿지 수수료 대납 여부(호출부가 안다). 가스 대납은 견적에서 읽는다. */\n  feeDelegated?: boolean;\n  /** 가스비를 첫 단계(= 실제로 가스가 나가는 체인)에 붙인다. 기본 뷰는 별도\n   *  \"Est. Tx Fee\" 섹션이 있으므로 끄고, embedded 는 그 섹션이 없어 켠다. */\n  includeTxFee?: boolean;\n}) {\n  const feeDelegated =\n    Boolean(summary.txFeeInfo?.isDelegateFee) || Boolean(feeDelegatedProp);\n  const sections = React.useMemo(() => {\n    const order: Array<[\"bridge\" | \"swap\", string]> =\n      summary.pathType === \"bridge-swap\"\n        ? [\n            [\"bridge\", \"Bridge\"],\n            [\"swap\", \"Swap\"],\n          ]\n        : [\n            [\"swap\", \"Swap\"],\n            [\"bridge\", \"Bridge\"],\n          ];\n\n    return order\n      .map(([key, label]) => {\n        const rows = key === \"swap\" ? summary.swapInfo : summary.bridgeInfo;\n        return rows?.length ? toRouteSection(rows, key, label) : null;\n      })\n      .filter((section): section is RouteSection => section !== null);\n  }, [summary]);\n\n  if (sections.length === 0) return null;\n\n  const first = sections[0];\n  const last = sections[sections.length - 1];\n  const sendToken =\n    first.inRow?.valueToken ??\n    (from?.symbol ? { symbol: from.symbol, iconUrl: from.iconUrl } : undefined);\n  const sendAmount = first.inRow?.value ?? from?.amount;\n  const receiveToken =\n    last.outRow?.valueToken ??\n    (to?.symbol ? { symbol: to.symbol, iconUrl: to.iconUrl } : undefined);\n  const receiveAmount = last.outRow?.value ?? to?.amount;\n\n  // 보내는 금액조차 없으면(입력 전) Info 자체가 필요 없다.\n  if (!sendAmount) return null;\n\n  const txFeeInfo = summary.txFeeInfo;\n  const gasMeta: RouteStepMeta[] =\n    includeTxFee && txFeeInfo && !txFeeInfo.isDelegateFee\n      ? [\n          {\n            text: `Gas ${txFeeInfo.estTxFee}${\n              txFeeInfo.tokenSymbol ? ` ${txFeeInfo.tokenSymbol}` : \"\"\n            }`,\n          },\n        ]\n      : [];\n\n  const multiHop = sections.length > 1;\n  const steps: RouteStep[] = [\n    {\n      key: \"send\",\n      title: `Send ${sendToken?.symbol ?? \"\"}`.trim(),\n      amount: sendAmount,\n      amountToken: sendToken,\n      routeTokens: multiHop ? undefined : first.routeTokens,\n      meta: [\n        // 가스를 이 단계에 붙일 땐 체인 대신 \"EST.\" 를 앞세운다 — 옆에 붙는\n        // 금액이 실제 청구액이 아니라 추정치라는 걸 라벨이 먼저 말하게 한다.\n        ...(gasMeta.length\n          ? [{ text: \"EST.\" }]\n          : from?.chain\n            ? [{ text: `on ${from.chain}` }]\n            : []),\n        ...gasMeta,\n        ...(multiHop\n          ? []\n          : [\n              ...(first.routeText ? [{ text: first.routeText }] : []),\n              ...first.meta.filter((item) => !IMPACT_ROW.test(item.text)),\n            ]),\n      ],\n      // 구간이 여럿이면 대납 태그는 수수료가 붙는 swap 구간에 단다(아래).\n      tags: feeDelegated && !multiHop ? [\"Fee delegated\"] : [],\n    },\n    ...(multiHop\n      ? sections.map((section) => ({\n          key: `hop-${section.key}`,\n          title: section.label,\n          amount: section.routeText ?? \"\",\n          routeTokens: section.routeTokens,\n          meta: section.meta,\n          tags:\n            feeDelegated && section.key === \"swap\" ? [\"Fee delegated\"] : [],\n        }))\n      : []),\n    {\n      key: \"receive\",\n      title: `Receive ${receiveToken?.symbol ?? \"\"}`.trim(),\n      // 견적이 오기 전에는 값만 비운다. 예전엔 이 사이에 기존 평면 목록으로\n      // 폴백해서, 숫자를 입력하면 옛 화면이 한 번 스쳤다.\n      amount: receiveAmount ?? \"—\",\n      amountToken: receiveToken,\n      meta: [\n        ...(to?.chain ? [{ text: `on ${to.chain}` }] : []),\n        ...(multiHop\n          ? []\n          : first.meta.filter((item) => IMPACT_ROW.test(item.text))),\n      ],\n      tags: [],\n    },\n  ];\n\n  const rest = [\n    ...sections.flatMap((section) => section.rest),\n    // Gas Price / Gas Limit 같은 세부 행은 단계 아래 목록에 남긴다.\n    ...(includeTxFee ? (summary.txFee ?? []) : []),\n  ].map(withImpactTone);\n  const showRate = Boolean(rateLine);\n\n  return (\n    <>\n      <RouteStepList steps={steps} />\n      {(rest.length > 0 || showRate) && (\n        <div className=\"bf-info-section\">\n          <InfoRows\n            rows={[\n              ...(showRate ? [{ label: \"Rate\", value: rateLine as string }] : []),\n              ...rest,\n            ]}\n          />\n        </div>\n      )}\n    </>\n  );\n}\n\nfunction EmbeddedBridgeDetails({\n  summary,\n  rateLine,\n  from,\n  to,\n  feeDelegated,\n}: {\n  summary: BridgeTxSummary;\n  rateLine?: string;\n  from?: RouteEndpoint;\n  to?: RouteEndpoint;\n  feeDelegated?: boolean;\n}) {\n  const normalized = React.useMemo(\n    () => normalizeEmbeddedSummary(summary),\n    [summary],\n  );\n  if (!normalized.bridgeInfo?.length && !normalized.swapInfo?.length) return null;\n  return (\n    <div className=\"bf-embedded-details\">\n      <RouteStepSections\n        summary={normalized}\n        rateLine={rateLine}\n        from={from}\n        to={to}\n        feeDelegated={feeDelegated}\n        includeTxFee\n      />\n    </div>\n  );\n}\n\nfunction EmbeddedFlowOverlays({\n  flow,\n  env,\n  toastError,\n}: {\n  flow: BridgeFlowState;\n  env: Environment;\n  toastError: string;\n}) {\n  return (\n    <>\n      <BridgeTermsModal flow={flow} env={env} />\n      <BridgeApproveModal flow={flow} approval={flow.approvalInfo ?? null} />\n      <BridgeApprovalSuccessModal flow={flow} />\n      <BridgeStatusToast flow={flow} />\n      <BridgeErrorToast message={flow.submitToast ? \"\" : toastError} />\n      <BridgeSuccessModal flow={flow} />\n    </>\n  );\n}\n\nfunction EmbeddedFormView({\n  flow,\n  env,\n  isConnected,\n  onRequestConnect,\n  feeDelegated,\n}: {\n  flow: BridgeFlowState;\n  env: Environment;\n  isConnected: boolean;\n  onRequestConnect?: () => void;\n  feeDelegated?: boolean;\n}) {\n  const hasAmount = Number(flow.fromAmount) > 0;\n  // `defaultSummary` 는 호출마다 새 객체를 만든다 — 그대로 두면 아래 상세\n  // 컴포넌트의 useMemo 가 매 렌더 무효화돼서 memo 를 둔 의미가 없어진다.\n  const summary = React.useMemo(\n    () => (hasAmount ? flow.summary ?? defaultSummary(flow) : null),\n    [hasAmount, flow],\n  );\n  const fieldErrors = getQuoteFieldErrors(flow.quoteError);\n  const insufficientBalance = exceedsBalance(flow.fromAmount, flow.fromToken);\n  const fromError = insufficientBalance ? \"Insufficient balance\" : fieldErrors.from;\n  const insufficientLiquidity = /insufficient liquidity|not enough liquidity/i.test(\n    flow.quoteError,\n  );\n  const toError = insufficientLiquidity ? undefined : fieldErrors.to;\n  const commonQuoteError =\n    flow.quoteError && !fieldErrors.from && !fieldErrors.to\n      ? flow.quoteError\n      : \"\";\n  const toastError = flow.error || commonQuoteError;\n  const isSubmitting = flow.status === \"submitting\";\n  const isDisabled =\n    !hasAmount ||\n    !flow.toAmount ||\n    !flow.fromToken ||\n    !flow.toToken ||\n    !flow.canSubmit ||\n    Boolean(flow.quoteError) ||\n    insufficientBalance ||\n    isSubmitting;\n\n  const actionLabel = isSubmitting\n    ? \"\"\n    : insufficientLiquidity && hasAmount\n      ? \"Insufficient Liquidity\"\n      : !hasAmount\n        ? \"Enter Amount\"\n        : \"Continue\";\n\n  return (\n    <>\n      <main className=\"bf-body bf-embedded-body\" data-rd-scroll-area>\n        <div className=\"bf-embedded-form\">\n          <div className=\"bf-embedded-fields\">\n            <div className=\"bf-embedded-field-group\">\n              <EmbeddedAmountPanel\n                label=\"From\"\n                token={flow.fromToken}\n                amount={flow.fromAmount}\n                onAmountChange={(value) => flow.setAmount(value, \"from\")}\n                onTokenClick={() => {\n                  const next = nextToken(flow.tokens, flow.fromToken);\n                  if (next) flow.selectFromToken(next);\n                }}\n                onMax={flow.setMax}\n                locked={flow.tokens.length <= 1}\n              />\n              {fromError && <div className=\"bf-embedded-field-error\">{fromError}</div>}\n            </div>\n\n            <div className=\"bf-embedded-direction\" aria-hidden=\"true\">\n              <span><ArrowDownIcon size={16} /></span>\n            </div>\n\n            <div className=\"bf-embedded-field-group\">\n              <EmbeddedAmountPanel\n                label=\"To\"\n                token={flow.toToken}\n                amount={flow.toAmount}\n                onAmountChange={(value) => flow.setAmount(value, \"to\")}\n                onTokenClick={() => {\n                  const next = nextToken(flow.availableToTokens, flow.toToken);\n                  if (next) flow.selectToToken(next);\n                }}\n                locked={flow.availableToTokens.length <= 1}\n              />\n              {toError && <div className=\"bf-embedded-field-error\">{toError}</div>}\n            </div>\n          </div>\n\n          <Collapse open={Boolean(summary?.liquidity)}>\n            {summary?.liquidity && (\n              <section className=\"bf-embedded-liquidity\">\n                <div>\n                  <span>{summary.liquidity.label ?? \"Available Liquidity\"}</span>\n                  <span data-status={summary.liquidity.status}>\n                    {summary.liquidity.status ?? \"normal\"}\n                  </span>\n                </div>\n                <span className=\"bf-embedded-liquidity-track\">\n                  <span style={{ width: `${summary.liquidity.percentage ?? 50}%` }} />\n                </span>\n              </section>\n            )}\n          </Collapse>\n\n          <Collapse open={insufficientLiquidity && hasAmount}>\n            <div className=\"bf-embedded-liquidity-error\" role=\"alert\">\n              Insufficient liquidity in all pools for this pair and amount.\n            </div>\n          </Collapse>\n\n          <Collapse open={Boolean(summary)}>\n            {summary && (\n            <div className=\"bf-embedded-summary\">\n              <EmbeddedBridgeDetails\n                summary={summary}\n                feeDelegated={feeDelegated}\n                rateLine={formatRateLine(\n                  flow.fromAmount,\n                  flow.toAmount,\n                  flow.fromToken,\n                  flow.toToken,\n                )}\n                from={{\n                  symbol: flow.fromToken?.symbol,\n                  iconUrl: flow.fromToken?.iconUrl,\n                  amount: formatAmountForStep(flow.fromAmount),\n                  chain: flow.fromToken\n                    ? chainLabel(flow.fromToken.chainId)\n                    : undefined,\n                }}\n                to={{\n                  symbol: flow.toToken?.symbol,\n                  iconUrl: flow.toToken?.iconUrl,\n                  amount: formatAmountForStep(flow.toAmount),\n                  chain: flow.toToken\n                    ? chainLabel(flow.toToken.chainId)\n                    : undefined,\n                }}\n              />\n            </div>\n            )}\n          </Collapse>\n\n          <div className=\"bf-embedded-action-footer\">\n            {isConnected ? (\n              <button\n                type=\"button\"\n                className=\"bf-embedded-cta\"\n                onClick={flow.submit}\n                data-track=\"next\"\n                disabled={isDisabled}\n                aria-busy={isSubmitting}\n                aria-label={\n                  isSubmitting\n                    ? \"Processing transaction\"\n                    : insufficientLiquidity\n                      ? \"Insufficient liquidity\"\n                      : \"Continue with swap or bridge\"\n                }\n              >\n                {isSubmitting ? (\n                  <span className=\"bf-embedded-spinner\" aria-hidden=\"true\" />\n                ) : (\n                  actionLabel\n                )}\n              </button>\n            ) : (\n              <button\n                type=\"button\"\n                className=\"bf-embedded-cta\"\n                onClick={onRequestConnect}\n                data-track=\"connect_click\"\n                aria-label=\"Connect wallet to continue\"\n              >\n                Connect Wallet\n              </button>\n            )}\n          </div>\n        </div>\n      </main>\n      <EmbeddedFlowOverlays\n        flow={flow}\n        env={env}\n        toastError={toastError}\n      />\n    </>\n  );\n}\n\nfunction HistoryCard({ item }: { item: BridgeHistoryItem }) {\n  const status = getHistoryStatus(item);\n  const fiatAmount = formatHistoryUsd(item.fromAmount, item.fromToken);\n  return (\n    <article className=\"bf-history-item\">\n      <div className=\"bf-history-item-head\">\n        <span className=\"bf-history-status\" data-status={status}>\n          {historyStatusLabel(status)}\n        </span>\n        <time className=\"bf-history-time\" dateTime={item.timestamp}>\n          {formatHistoryTimestamp(item.timestamp)}\n        </time>\n      </div>\n      <div className=\"bf-history-item-body\">\n        <strong className=\"bf-history-amount\">\n          {formatHistoryTokenAmount(item.fromAmount)} {item.fromToken.symbol}\n        </strong>\n        <div className=\"bf-history-meta\">\n          <span className=\"bf-history-usd\">{fiatAmount ?? shortHash(item.txHash)}</span>\n          <span className=\"bf-history-route\">\n            <span className=\"bf-history-route-token\">\n              <TokenIcon token={item.fromToken} size={16} />\n              <span>{item.fromToken.symbol}</span>\n            </span>\n            <ArrowRightIcon size={16} className=\"bf-history-route-arrow\" />\n            <span className=\"bf-history-route-token\">\n              <TokenIcon token={item.toToken} size={16} />\n              <span>{item.toToken.symbol}</span>\n            </span>\n          </span>\n        </div>\n      </div>\n    </article>\n  );\n}\n\nfunction HistoryView({ flow }: { flow: BridgeFlowState }) {\n  const [filter, setFilter] = React.useState<HistoryFilter>(\"all\");\n  const hasPending = flow.history.some((item) => getHistoryStatus(item) === \"pending\");\n  const visibleHistory = flow.history.filter((item) => historyMatchesFilter(item, filter));\n  const emptyMessage =\n    filter === \"all\"\n      ? \"No bridge history yet.\"\n      : `No ${filter} bridge history.`;\n\n  return (\n    <main className=\"bf-history-body\">\n      <nav className=\"bf-history-tabs\" aria-label=\"Bridge history filters\">\n        {HISTORY_FILTERS.map((item) => (\n          <button\n            key={item.value}\n            type=\"button\"\n            className=\"bf-history-tab\"\n            data-active={filter === item.value ? \"true\" : \"false\"}\n            onClick={() => setFilter(item.value)}\n            data-track=\"history_filter\"\n            data-track-id={item.value}\n          >\n            <span>{item.label}</span>\n            {item.value === \"pending\" && hasPending && (\n              <span className=\"bf-history-tab-dot\" aria-hidden=\"true\" />\n            )}\n          </button>\n        ))}\n      </nav>\n      {visibleHistory.length > 0 ? (\n        <div className=\"bf-history-list\">\n          {visibleHistory.map((item) => (\n            <HistoryCard key={item.id || item.txHash} item={item} />\n          ))}\n        </div>\n      ) : (\n        <div className=\"bf-empty\">{emptyMessage}</div>\n      )}\n    </main>\n  );\n}\n\nfunction DefaultFormView({\n  flow,\n  env,\n  feeDelegated,\n}: {\n  flow: BridgeFlowState;\n  env: Environment;\n  feeDelegated?: boolean;\n}) {\n  const [slippageSettingsOpen, setSlippageSettingsOpen] = React.useState(false);\n  const [swapRotation, setSwapRotation] = React.useState(0);\n  const hasAmount = Boolean(flow.fromAmount.trim() || flow.toAmount.trim());\n  // `defaultSummary` 는 호출마다 새 객체를 만든다 — 그대로 두면 아래 상세\n  // 컴포넌트의 useMemo 가 매 렌더 무효화돼서 memo 를 둔 의미가 없어진다.\n  const summary = React.useMemo(\n    () => (hasAmount ? flow.summary ?? defaultSummary(flow) : null),\n    [hasAmount, flow],\n  );\n  // apps.json(bridgePoweredBy.url)이 단일 소스. 로드 전에는 링크 미표시.\n  const poweredByUrl = useRemoteConfigValue<AppsConfig, string>(\n    \"apps.json\",\n    env,\n    (config) => resolveEnvValue(config?.bridgePoweredBy?.url, env),\n  );\n  const fieldErrors = getQuoteFieldErrors(flow.quoteError);\n  const insufficientBalance = exceedsBalance(flow.fromAmount, flow.fromToken);\n  const fromError = insufficientBalance ? \"Insufficient balance\" : fieldErrors.from;\n  const commonQuoteError =\n    flow.quoteError && !fieldErrors.from && !fieldErrors.to\n      ? flow.quoteError\n      : \"\";\n  const toastError = flow.error || commonQuoteError;\n  // Show the dot whenever history has a pending tx (persists across reloads),\n  // or a freshly-submitted tx is awaiting its first view.\n  const hasPendingHistory = flow.history.some(\n    (item) => getHistoryStatus(item) === \"pending\",\n  );\n  // Slippage only applies to swap-bearing routes. A pure cross-chain bridge of\n  // the same asset (e.g. CROSS↔CROSS, USDT↔USDT) has no swap, so hide it there.\n  const slippagePathType =\n    summary?.pathType ?? inferPathType(flow.fromToken, flow.toToken);\n  const showSlippage = slippagePathType !== \"bridge\";\n\n  return (\n    <>\n      <main className=\"bf-body\">\n        <div className=\"bf-toolbar\">\n          <button\n            type=\"button\"\n            className=\"bf-chip bf-chip-history\"\n            onClick={() => flow.setStep(\"history\")}\n            data-track=\"history_open\"\n          >\n            <HistoryIcon />\n            <span>History</span>\n            {(hasPendingHistory || flow.hasNewHistory) && (\n              <span className=\"bf-chip-dot\" aria-hidden=\"true\" />\n            )}\n          </button>\n          {showSlippage && (\n            <button\n              type=\"button\"\n              className=\"bf-chip\"\n              onClick={() => setSlippageSettingsOpen(true)}\n              data-track=\"slippage_open\"\n              disabled={flow.slippageLocked}\n              aria-haspopup=\"dialog\"\n              aria-expanded={slippageSettingsOpen}\n            >\n              <SlidersIcon />\n              <span>\n                {flow.slippageLocked\n                  ? \"Auto 1%\"\n                  : formatSlippageLabel(flow.slippage, flow.effectiveSlippage)}\n              </span>\n            </button>\n          )}\n        </div>\n\n        <div\n          className=\"bf-swap-card\"\n          data-has-from-error={fromError ? \"true\" : \"false\"}\n        >\n          <BridgeAmountPanel\n            label=\"From\"\n            token={flow.fromToken}\n            amount={flow.fromAmount}\n            usd={formatUsd(flow.fromAmount, flow.fromToken)}\n            onAmountChange={(value) => flow.setAmount(value, \"from\")}\n            onTokenClick={() => {\n              const next = nextToken(flow.tokens, flow.fromToken);\n              if (next) flow.selectFromToken(next);\n            }}\n            locked={flow.tokens.length <= 1}\n          />\n          {fromError && (\n            <div className=\"bf-field-error\">{fromError}</div>\n          )}\n          <button\n            type=\"button\"\n            className=\"bf-swap-toggle\"\n            onClick={() => { flow.swapTokens(); setSwapRotation((r) => r + 360); }}\n            data-track=\"token_swap\"\n            style={{ transform: `translateX(-50%) rotate(${swapRotation}deg)`, transition: \"transform 0.2s ease\" }}\n          >\n            <ArrowDownIcon size={22} />\n          </button>\n          <BridgeAmountPanel\n            label=\"To\"\n            token={flow.toToken}\n            amount={flow.toAmount}\n            usd={formatUsd(flow.toAmount, flow.toToken)}\n            onAmountChange={(value) => flow.setAmount(value, \"to\")}\n            onTokenClick={() => {\n              const next = nextToken(flow.availableToTokens, flow.toToken);\n              if (next) flow.selectToToken(next);\n            }}\n            locked={flow.availableToTokens.length <= 1}\n          />\n          {fieldErrors.to && (\n            <div className=\"bf-field-error\">{fieldErrors.to}</div>\n          )}\n        </div>\n\n        <ExchangeRate\n          fromAmount={flow.fromAmount}\n          toAmount={flow.toAmount}\n          fromToken={flow.fromToken}\n          toToken={flow.toToken}\n        />\n\n        <Collapse open={Boolean(summary)}>\n          {summary && (\n            <BridgeDetails\n              summary={summary}\n              feeDelegated={feeDelegated}\n              from={{\n                symbol: flow.fromToken?.symbol,\n                iconUrl: flow.fromToken?.iconUrl,\n                amount: formatAmountForStep(flow.fromAmount),\n                chain: flow.fromToken\n                  ? chainLabel(flow.fromToken.chainId)\n                  : undefined,\n              }}\n              to={{\n                symbol: flow.toToken?.symbol,\n                iconUrl: flow.toToken?.iconUrl,\n                amount: formatAmountForStep(flow.toAmount),\n                chain: flow.toToken ? chainLabel(flow.toToken.chainId) : undefined,\n              }}\n            />\n          )}\n        </Collapse>\n      </main>\n      <footer className=\"bf-footer\">\n        <button\n          type=\"button\"\n          className=\"bf-next\"\n          onClick={flow.submit}\n          data-track=\"next\"\n          disabled={flow.status === \"submitting\" || Boolean(flow.quoteError) || insufficientBalance}\n        >\n          {flow.status === \"submitting\" ? \"Processing...\" : \"Next\"}\n        </button>\n        {poweredByUrl && (\n          <a\n            className=\"bf-powered\"\n            href={poweredByUrl}\n            target=\"_blank\"\n            rel=\"noreferrer\"\n          >\n            Powered by <span className=\"bf-powered-brand\">CROSS DeFi</span>\n          </a>\n        )}\n      </footer>\n      {slippageSettingsOpen && (\n        <BridgeSlippageSettings\n          flow={flow}\n          onClose={() => setSlippageSettingsOpen(false)}\n        />\n      )}\n      <BridgeTermsModal flow={flow} env={env} />\n      <BridgeApproveModal flow={flow} approval={flow.approvalInfo ?? null} />\n      <BridgeApprovalSuccessModal flow={flow} />\n      <BridgeStatusToast flow={flow} />\n      <BridgeErrorToast message={flow.submitToast ? \"\" : toastError} />\n      <BridgeSuccessModal flow={flow} />\n    </>\n  );\n}\n\nexport function BridgeFlow({\n  onClose,\n  onBackToWallet,\n  feeDelegated,\n  env,\n  variant = \"default\",\n  title: titleProp,\n  isConnected: isConnectedProp,\n  onRequestConnect,\n  className,\n  ...rest\n}: BridgeFlowProps) {\n  const resolvedEnv = React.useMemo(() => resolveEnvironment(env), [env]);\n  const isSoftKeyboardOpen = useSoftKeyboardOpen();\n  const flow = useBridgeFlow({\n    ...rest,\n    onExit: onBackToWallet,\n  });\n\n  const rootRef = React.useRef<HTMLDivElement>(null);\n  const theme = useDetectTheme(rootRef);\n  const title = flow.step === \"history\" ? \"History\" : titleProp ?? \"Swap & Bridge\";\n  const isConnected = isConnectedProp ?? Boolean(rest.walletAddress);\n  const backAction =\n    variant === \"embedded\" && flow.step === \"form\" && !onBackToWallet\n      ? null\n      : flow.back;\n\n  return (\n    <TrackingBoundary feature=\"bridge\">\n    <ThemeContext.Provider value={theme}>\n    <div\n      ref={rootRef}\n      className={cn(\"bf-root\", className)}\n      data-view={flow.step}\n      data-status={flow.status}\n      data-variant={variant}\n      data-soft-keyboard-open={\n        variant === \"embedded\" && isSoftKeyboardOpen ? \"true\" : \"false\"\n      }\n    >\n      <header className=\"bf-header\">\n        {/* Embedded form has nowhere to go back to unless the host gives us an\n            exit (e.g. Get ONEUSD returns to its action chooser). */}\n        {!backAction ? (\n          <span className=\"bf-header-spacer\" aria-hidden=\"true\" />\n        ) : (\n          <button type=\"button\" className=\"bf-header-btn\" onClick={backAction} aria-label=\"Back\">\n            <BackIcon size={24} />\n          </button>\n        )}\n        {/* Bigger hit target for the back arrow. Keyboard/a11y stays on the\n            button, so the title stays a plain span. */}\n        <span\n          className=\"bf-header-title\"\n          data-clickable={backAction ? \"true\" : \"false\"}\n          onClick={backAction ?? undefined}\n        >\n          {title}\n        </span>\n        <button type=\"button\" className=\"bf-header-btn\" onClick={onClose} aria-label=\"Close\">\n          <CloseIcon size={24} />\n        </button>\n      </header>\n\n      {flow.step === \"history\" ? (\n        <HistoryView flow={flow} />\n      ) : variant === \"embedded\" ? (\n        <EmbeddedFormView\n          flow={flow}\n          env={resolvedEnv}\n          isConnected={isConnected}\n          onRequestConnect={onRequestConnect}\n          feeDelegated={feeDelegated}\n        />\n      ) : (\n        <DefaultFormView\n          flow={flow}\n          env={resolvedEnv}\n          feeDelegated={feeDelegated}\n        />\n      )}\n    </div>\n    </ThemeContext.Provider>\n    </TrackingBoundary>\n  );\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nconst KEYBOARD_OPEN_THRESHOLD_PX = 120;\n\nfunction isTextInputTarget(target: EventTarget | null): boolean {\n  return (\n    target instanceof HTMLElement &&\n    target.matches(\"input, textarea, select\")\n  );\n}\n\n/**\n * Detects a real mobile soft keyboard from the visual viewport height delta.\n * A focus event alone is insufficient because hardware keyboards do not resize\n * the viewport. Orientation changes reset the baseline to avoid false positives.\n */\nexport function useSoftKeyboardOpen(): boolean {\n  const [isOpen, setIsOpen] = React.useState(false);\n\n  React.useEffect(() => {\n    const viewport = window.visualViewport;\n    const readHeight = () => viewport?.height ?? window.innerHeight;\n    let baselineHeight = readHeight();\n\n    const syncFromViewport = () => {\n      const height = readHeight();\n      baselineHeight = Math.max(baselineHeight, height);\n      setIsOpen(baselineHeight - height > KEYBOARD_OPEN_THRESHOLD_PX);\n    };\n\n    const resetBaseline = () => {\n      baselineHeight = readHeight();\n      setIsOpen(false);\n    };\n\n    const handleFocusIn = (event: FocusEvent) => {\n      if (\n        isTextInputTarget(event.target) &&\n        baselineHeight - readHeight() > KEYBOARD_OPEN_THRESHOLD_PX\n      ) {\n        setIsOpen(true);\n      }\n    };\n\n    const handleFocusOut = (event: FocusEvent) => {\n      if (isTextInputTarget(event.target)) setIsOpen(false);\n    };\n\n    syncFromViewport();\n    viewport?.addEventListener(\"resize\", syncFromViewport);\n    window.addEventListener(\"resize\", syncFromViewport);\n    window.screen?.orientation?.addEventListener(\"change\", resetBaseline);\n    document.addEventListener(\"focusin\", handleFocusIn);\n    document.addEventListener(\"focusout\", handleFocusOut);\n\n    return () => {\n      viewport?.removeEventListener(\"resize\", syncFromViewport);\n      window.removeEventListener(\"resize\", syncFromViewport);\n      window.screen?.orientation?.removeEventListener(\"change\", resetBaseline);\n      document.removeEventListener(\"focusin\", handleFocusIn);\n      document.removeEventListener(\"focusout\", handleFocusOut);\n    };\n  }, []);\n\n  return isOpen;\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Keeps the last truthy `value` around for `ms` after it goes falsy, so a\n * conditionally-rendered overlay can run a CSS exit animation before React\n * unmounts it.\n *\n * Returns the value to render with (`null` once the exit animation is done) and\n * the animation state to stamp on the element as `data-state`.\n */\nexport function useExitDelay<T>(\n  value: T,\n  ms = 160,\n): [T | null, \"open\" | \"closed\"] {\n  const [held, setHeld] = React.useState<T | null>(value ? value : null);\n\n  React.useEffect(() => {\n    if (value) {\n      setHeld(value);\n      return undefined;\n    }\n    const timer = window.setTimeout(() => setHeld(null), ms);\n    return () => window.clearTimeout(timer);\n  }, [value, ms]);\n\n  return [value ? value : held, value ? \"open\" : \"closed\"];\n}\n","interface IconProps {\n  size?: number;\n  className?: string;\n}\n\nexport function SearchIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <circle cx=\"9\" cy=\"9\" r=\"6\" stroke=\"currentColor\" strokeWidth={1.7} />\n      <path\n        d=\"m17 17-3.2-3.2\"\n        stroke=\"currentColor\"\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function HistoryIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M2.667 8a5.333 5.333 0 1 0 1.562-3.771L2.667 5.79M2.667 2.667v3.124h3.124M8 5.333V8l1.667 1\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function SlidersIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M2.667 4h4M9.333 4h4M6.667 2.667v2.666M2.667 12h4M9.333 12h4M9.333 10.667v2.666M2.667 8h1.666M7 8h6.333M4.333 6.667v2.666\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ArrowDownIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M10 4.167v11.666M4.167 10 10 15.833 15.833 10\"\n        stroke=\"currentColor\"\n        strokeWidth={1.667}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function ArrowRightIcon({ size = 16, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M3.333 8h9.334M8.667 4l4 4-4 4\"\n        stroke=\"currentColor\"\n        strokeWidth={1.333}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n  createBridgeRepository,\n  type BridgeRepositoryDeps,\n} from \"./BridgeRepository\";\nimport type {\n  BridgeAmountSource,\n  BridgeApprovalInfo,\n  BridgeApproveFn,\n  BridgeFailedInfo,\n  BridgeGetApprovalFn,\n  BridgeGetToTokensFn,\n  BridgeHistoryItem,\n  BridgeQuoteInput,\n  BridgeStatus,\n  BridgeStep,\n  BridgeSubmittedInfo,\n  BridgeToken,\n  BridgeTxSummary,\n} from \"../types\";\nimport { normalizeFailureReason, trackDappUiFunnel } from \"../../../lib/sentry\";\n\n// Input limits for the amount field: max 15 integer digits, max 18 decimal digits.\nconst AMOUNT_MAX_INTEGER_DIGITS = 15;\nconst AMOUNT_MAX_DECIMAL_DIGITS = 18;\nconst TERMS_ACCEPTED_COOKIE_NAME = \"cross_bridge_terms_accepted\";\nconst TERMS_ACCEPTED_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;\n\nfunction sanitizeAmountInput(value: string): string {\n  let cleaned = value.replace(/[^\\d.]/g, \"\");\n  // Keep only the first decimal point.\n  const firstDot = cleaned.indexOf(\".\");\n  if (firstDot !== -1) {\n    cleaned =\n      cleaned.slice(0, firstDot + 1) +\n      cleaned.slice(firstDot + 1).replace(/\\./g, \"\");\n  }\n  const dotIndex = cleaned.indexOf(\".\");\n  if (dotIndex === -1) {\n    return cleaned.slice(0, AMOUNT_MAX_INTEGER_DIGITS);\n  }\n  const intPart = cleaned.slice(0, dotIndex).slice(0, AMOUNT_MAX_INTEGER_DIGITS);\n  const decPart = cleaned.slice(dotIndex + 1).slice(0, AMOUNT_MAX_DECIMAL_DIGITS);\n  return `${intPart}.${decPart}`;\n}\n\nfunction hasAcceptedTermsCookie(): boolean {\n  if (typeof document === \"undefined\") return false;\n  return document.cookie\n    .split(\";\")\n    .map((cookie) => cookie.trim())\n    .some((cookie) => cookie === `${TERMS_ACCEPTED_COOKIE_NAME}=true`);\n}\n\nfunction persistAcceptedTermsCookie(): void {\n  if (typeof document === \"undefined\") return;\n  const secure =\n    typeof window !== \"undefined\" && window.location.protocol === \"https:\"\n      ? \"; Secure\"\n      : \"\";\n  document.cookie = [\n    `${TERMS_ACCEPTED_COOKIE_NAME}=true`,\n    \"Path=/\",\n    `Max-Age=${TERMS_ACCEPTED_COOKIE_MAX_AGE}`,\n    \"SameSite=Lax\",\n    secure,\n  ].filter(Boolean).join(\"; \");\n}\n\nexport interface UseBridgeFlowOptions extends BridgeRepositoryDeps {\n  tokens: BridgeToken[];\n  initialFromToken?: BridgeToken;\n  initialToToken?: BridgeToken;\n  getToTokens?: BridgeGetToTokensFn;\n  getApproval?: BridgeGetApprovalFn;\n  approveBridge?: BridgeApproveFn;\n  history?: BridgeHistoryItem[];\n  initialSlippage?: string;\n  termsUrl?: string;\n  onSubmitted?: (info: BridgeSubmittedInfo) => void;\n  onSuccess?: (info: BridgeSubmittedInfo) => void;\n  onFailed?: (info: BridgeFailedInfo) => void;\n  onExit?: () => void;\n}\n\n// crossd's Auto-slippage formula (swap-bridge/constants + SwapBridgeCard):\n//   impact < 0.1%      → 1%        (AUTO_SLIPPAGE_MIN)\n//   0.1% ≤ impact < 3% → impact×3, floored to 2dp, but never below 1%\n//   impact ≥ 3%        → 10%       (AUTO_SLIPPAGE_MAX)\n// `priceImpact` is the raw percent string from the quote summary (e.g. \"0.5\").\nfunction resolveAutoSlippage(priceImpact?: string): string {\n  const impact = Number(priceImpact ?? \"0\");\n  if (!Number.isFinite(impact) || impact < 0.1) return \"1\";\n  const trunc2 = (n: number) => Math.floor(n * 100) / 100;\n  if (impact < 3) {\n    const calc = trunc2(impact * 3);\n    return String(calc < 1 ? 1 : calc);\n  }\n  return \"10\";\n}\n\n// Normalize a native-family symbol, dropping the testnet \"t\" prefix\n// (e.g. \"tONEUSD\" → \"ONEUSD\", \"tONE\" → \"ONE\").\nfunction normalizeCrossSymbol(symbol: string): string {\n  const s = symbol.toUpperCase();\n  return s.startsWith(\"T\") && s.slice(1).startsWith(\"ONE\") ? s.slice(1) : s;\n}\n\n// The ONE↔ONEUSD pair swaps through a Uniswap v3 pool, which doesn't expose a\n// user-selectable slippage — it's fixed to Auto 1%. Detected by symbol on the\n// same chain (ONEUSD only lives on the CROSS chain), testnet prefix aside.\nfunction isUniswapV3Pair(a?: BridgeToken, b?: BridgeToken): boolean {\n  if (!a || !b || a.chainId !== b.chainId) return false;\n  const symbols = new Set([\n    normalizeCrossSymbol(a.symbol),\n    normalizeCrossSymbol(b.symbol),\n  ]);\n  return symbols.has(\"ONE\") && symbols.has(\"ONEUSD\");\n}\n\nfunction sameToken(a?: BridgeToken, b?: BridgeToken): boolean {\n  return (\n    !!a &&\n    !!b &&\n    a.chainId === b.chainId &&\n    a.address.toLowerCase() === b.address.toLowerCase()\n  );\n}\n\nfunction firstToken(tokens: BridgeToken[]): BridgeToken | undefined {\n  return tokens[0];\n}\n\nfunction selectDefaultToToken(\n  tokens: BridgeToken[],\n  from?: BridgeToken,\n  explicit?: BridgeToken,\n  getToTokens?: BridgeGetToTokensFn,\n): BridgeToken | undefined {\n  if (explicit && !sameToken(explicit, from)) return explicit;\n  const available = from ? getToTokens?.(from, tokens) : undefined;\n  if (available?.length) return available[0];\n  if (!from) return tokens[1] ?? firstToken(tokens);\n  const fromIndex = tokens.findIndex((token) => sameToken(token, from));\n  const nextToken = fromIndex >= 0 ? tokens[fromIndex + 1] : undefined;\n  if (nextToken && !sameToken(nextToken, from)) return nextToken;\n  return (\n    tokens.find((token) => !sameToken(token, from)) ??\n    from\n  );\n}\n\nfunction buildInput({\n  fromToken,\n  toToken,\n  fromAmount,\n  toAmount,\n  lastChangedBy,\n  slippage,\n}: {\n  fromToken: BridgeToken;\n  toToken: BridgeToken;\n  fromAmount: string;\n  toAmount: string;\n  lastChangedBy: BridgeAmountSource;\n  slippage: string;\n}): BridgeQuoteInput {\n  return {\n    fromToken,\n    toToken,\n    fromAmount,\n    toAmount,\n    lastChangedBy,\n    slippage,\n  };\n}\n\n// The user declined the request in their wallet (not a real failure).\nfunction collectErrorFields(err: unknown, depth = 0): string[] {\n  if (!err || depth > 5) return [];\n  if (typeof err === \"string\") return [err];\n  if (!(err instanceof Error) && typeof err !== \"object\") return [String(err)];\n\n  const errorLike = err as {\n    name?: unknown;\n    message?: unknown;\n    shortMessage?: unknown;\n    details?: unknown;\n    code?: unknown;\n    cause?: unknown;\n    data?: unknown;\n    error?: unknown;\n  };\n  const fields = [\n    err instanceof Error ? err.name : errorLike.name,\n    err instanceof Error ? err.message : errorLike.message,\n    errorLike.shortMessage,\n    errorLike.details,\n    errorLike.code,\n  ]\n    .filter((value): value is string | number => typeof value === \"string\" || typeof value === \"number\")\n    .map(String);\n\n  return [\n    ...fields,\n    ...collectErrorFields(errorLike.cause, depth + 1),\n    ...collectErrorFields(errorLike.data, depth + 1),\n    ...collectErrorFields(errorLike.error, depth + 1),\n  ];\n}\n\nfunction isUserRejection(err: unknown): boolean {\n  const haystack = collectErrorFields(err).join(\" \").toLowerCase();\n  const isCrossxTypedDataSignatureFailure =\n    haystack.includes(\"signature_failed\") &&\n    (haystack.includes(\"타입 데이터 서명에 실패\") ||\n      haystack.includes(\"typed data\") ||\n      haystack.includes(\"signtypeddata\"));\n  return (\n    haystack.includes(\"4001\") ||\n    haystack.includes(\"err_user_rejected\") ||\n    haystack.includes(\"userrejectedrequesterror\") ||\n    haystack.includes(\"user rejected\") ||\n    haystack.includes(\"user denied\") ||\n    haystack.includes(\"user cancelled\") ||\n    haystack.includes(\"user canceled\") ||\n    haystack.includes(\"rejected the request\") ||\n    haystack.includes(\"request rejected\") ||\n    haystack.includes(\"action_rejected\") ||\n    haystack.includes(\"rejected by user\") ||\n    isCrossxTypedDataSignatureFailure\n  );\n}\n\nfunction bridgeErrorMessage(err: unknown, fallback: string): string {\n  if (isUserRejection(err)) {\n    return \"Transaction request was rejected.\";\n  }\n  const raw =\n    err instanceof Error\n      ? err.message\n      : typeof err === \"string\"\n        ? err\n        : \"\";\n  return raw.split(\"\\n\")[0]?.trim() || fallback;\n}\n\nexport function useBridgeFlow(options: UseBridgeFlowOptions) {\n  const {\n    tokens,\n    initialFromToken,\n    initialToToken,\n    getToTokens,\n    getApproval,\n    approveBridge,\n    history = [],\n    initialSlippage = \"1\",\n    // 미설정 시 apps.json(legal.termsUrl) → DEFAULT_TERMS_URL 순으로 UI에서\n    // 폴백한다. 여기서 기본값을 넣으면 \"미설정\"을 구분할 수 없어 그대로 둔다.\n    termsUrl,\n    getQuote,\n    submitBridge,\n    onSubmitted,\n    onSuccess,\n    onFailed,\n    onExit,\n  } = options;\n\n  const repository = React.useMemo(\n    () => createBridgeRepository({ getQuote, submitBridge }),\n    [getQuote, submitBridge],\n  );\n\n  const [step, setStep] = React.useState<BridgeStep>(\"form\");\n  const [fromToken, setFromToken] = React.useState<BridgeToken | undefined>(\n    initialFromToken ?? firstToken(tokens),\n  );\n  const [toToken, setToToken] = React.useState<BridgeToken | undefined>(() =>\n    selectDefaultToToken(\n      tokens,\n      initialFromToken ?? firstToken(tokens),\n      initialToToken,\n      getToTokens,\n    ),\n  );\n  const [fromAmount, setFromAmount] = React.useState(\"\");\n  const [toAmount, setToAmount] = React.useState(\"\");\n  const [lastChangedBy, setLastChangedBy] =\n    React.useState<BridgeAmountSource>(\"from\");\n  const [slippage, setSlippage] = React.useState(initialSlippage);\n  const [summary, setSummary] = React.useState<BridgeTxSummary | null>(null);\n  // The concrete percent used for amountOutMinimum at submit. \"auto\" resolves\n  // via crossd's price-impact formula; an explicit preset/custom passes through.\n  // Note: this only affects the submitted tx (slippage doesn't change the quote\n  // amounts or the displayed gas estimate), so feeding it back into the quote\n  // would be pointless — the quote keeps using the raw `slippage` mode.\n  // Uniswap v3 pairs (ONE↔ONEUSD) ignore the chosen slippage and lock to 1%.\n  const slippageLocked = React.useMemo(\n    () => isUniswapV3Pair(fromToken, toToken),\n    [fromToken, toToken],\n  );\n  const effectiveSlippage = React.useMemo(\n    () =>\n      slippageLocked\n        ? \"1\"\n        : slippage === \"auto\"\n          ? resolveAutoSlippage(summary?.priceImpact)\n          : slippage,\n    [slippageLocked, slippage, summary?.priceImpact],\n  );\n  const [quoteError, setQuoteError] = React.useState(\"\");\n  const [status, setStatus] = React.useState<BridgeStatus>(\"idle\");\n  const [error, setError] = React.useState(\"\");\n  const [submitted, setSubmitted] = React.useState<BridgeSubmittedInfo | null>(\n    null,\n  );\n  const [localHistory, setLocalHistory] = React.useState<BridgeHistoryItem[]>([]);\n  const [termsOpen, setTermsOpen] = React.useState(false);\n  const [termsAccepted, setTermsAccepted] = React.useState(\n    hasAcceptedTermsCookie,\n  );\n  const [approvalInfo, setApprovalInfo] =\n    React.useState<BridgeApprovalInfo | null>(null);\n  const [approvalSuccessOpen, setApprovalSuccessOpen] = React.useState(false);\n  const [pendingSubmitInput, setPendingSubmitInput] =\n    React.useState<BridgeQuoteInput | null>(null);\n  const [submitToast, setSubmitToast] =\n    React.useState<\"processing\" | null>(null);\n  const [successDialogOpen, setSuccessDialogOpen] = React.useState(false);\n  const [hasNewHistory, setHasNewHistory] = React.useState(false);\n  const quoteReqIdRef = React.useRef(0);\n  const toastTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n  const submitLockRef = React.useRef(false);\n  const approvalLockRef = React.useRef(false);\n  const funnelOpenedRef = React.useRef(false);\n  const funnelTokenSelectedRef = React.useRef(false);\n  const funnelAmountEnteredRef = React.useRef(false);\n  const funnelSubmittedRef = React.useRef(false);\n  const funnelApprovedRef = React.useRef(false);\n\n  React.useEffect(() => {\n    if (funnelOpenedRef.current) return;\n    funnelOpenedRef.current = true;\n    trackDappUiFunnel(\"bridge\", \"opened\");\n  }, []);\n\n  const clearToastTimer = React.useCallback(() => {\n    if (!toastTimerRef.current) return;\n    clearTimeout(toastTimerRef.current);\n    toastTimerRef.current = null;\n  }, []);\n\n  const showSubmitToast = React.useCallback(\n    (next: \"processing\" | null) => {\n      clearToastTimer();\n      setSubmitToast(next);\n    },\n    [clearToastTimer],\n  );\n\n  React.useEffect(() => clearToastTimer, [clearToastTimer]);\n\n  React.useEffect(() => {\n    const nextFrom =\n      tokens.find((token) => sameToken(token, fromToken)) ?? firstToken(tokens);\n    const available = nextFrom ? getToTokens?.(nextFrom, tokens) : undefined;\n    const nextTo =\n      available?.find((token) => sameToken(token, toToken)) ??\n      (!available ? tokens.find((token) => sameToken(token, toToken)) : undefined) ??\n      selectDefaultToToken(tokens, nextFrom, undefined, getToTokens);\n    setFromToken(nextFrom);\n    setToToken(nextTo);\n  }, [getToTokens, tokens, fromToken, toToken]);\n\n  const setAmount = React.useCallback(\n    (value: string, source: BridgeAmountSource) => {\n      const sanitized = sanitizeAmountInput(value);\n      if (!sanitized) {\n        quoteReqIdRef.current += 1;\n        setFromAmount(\"\");\n        setToAmount(\"\");\n        setLastChangedBy(source);\n        setSummary(null);\n        setQuoteError(\"\");\n        setStatus(\"idle\");\n        return;\n      }\n      if (source === \"from\") {\n        setFromAmount(sanitized);\n      } else {\n        setToAmount(sanitized);\n      }\n      setLastChangedBy(source);\n      setQuoteError(\"\");\n      if (!funnelAmountEnteredRef.current && Number(sanitized) > 0) {\n        funnelAmountEnteredRef.current = true;\n        trackDappUiFunnel(\"bridge\", \"amount_entered\");\n      }\n    },\n    [],\n  );\n\n  const resetQuoteState = React.useCallback(() => {\n    quoteReqIdRef.current += 1;\n    setFromAmount(\"\");\n    setToAmount(\"\");\n    setLastChangedBy(\"from\");\n    setSummary(null);\n    setQuoteError(\"\");\n    setError(\"\");\n    setStatus(\"idle\");\n    setApprovalInfo(null);\n    setApprovalSuccessOpen(false);\n    setPendingSubmitInput(null);\n    setSuccessDialogOpen(false);\n    showSubmitToast(null);\n  }, [showSubmitToast]);\n\n  const selectFromToken = React.useCallback(\n    (token: BridgeToken) => {\n      if (sameToken(token, fromToken)) return;\n      if (!funnelTokenSelectedRef.current) {\n        funnelTokenSelectedRef.current = true;\n        trackDappUiFunnel(\"bridge\", \"token_selected\", {\n          tags: { item_id: `from:${token.symbol}` },\n        });\n      }\n      setFromToken(token);\n      setToToken(selectDefaultToToken(tokens, token, undefined, getToTokens));\n      resetQuoteState();\n    },\n    [fromToken, getToTokens, resetQuoteState, tokens],\n  );\n\n  const selectToToken = React.useCallback(\n    (token: BridgeToken) => {\n      if (sameToken(token, toToken) || sameToken(token, fromToken)) return;\n      if (!funnelTokenSelectedRef.current) {\n        funnelTokenSelectedRef.current = true;\n        trackDappUiFunnel(\"bridge\", \"token_selected\", {\n          tags: { item_id: `to:${token.symbol}` },\n        });\n      }\n      setToToken(token);\n      resetQuoteState();\n    },\n    [fromToken, resetQuoteState, toToken],\n  );\n\n  const availableToTokens = React.useMemo(() => {\n    if (!fromToken) return [];\n    return getToTokens?.(fromToken, tokens) ?? tokens.filter((token) => !sameToken(token, fromToken));\n  }, [fromToken, getToTokens, tokens]);\n\n  const setMax = React.useCallback(() => {\n    if (!fromToken?.balance) return;\n    setAmount(fromToken.balance, \"from\");\n  }, [fromToken?.balance, setAmount]);\n\n  const swapTokens = React.useCallback(() => {\n    if (!fromToken || !toToken) return;\n    setFromToken(toToken);\n    setToToken(fromToken);\n    resetQuoteState();\n  }, [fromToken, resetQuoteState, toToken]);\n\n  const resetInputAfterSubmit = React.useCallback(() => {\n    quoteReqIdRef.current += 1;\n    setFromAmount(\"\");\n    setToAmount(\"\");\n    setLastChangedBy(\"from\");\n    setSummary(null);\n    setQuoteError(\"\");\n    setError(\"\");\n  }, []);\n\n  React.useEffect(() => {\n    if (!fromToken || !toToken) return;\n    if (!fromAmount && !toAmount) {\n      setSummary(null);\n      setQuoteError(\"\");\n      setStatus((current) => (current === \"quoting\" ? \"idle\" : current));\n      return;\n    }\n\n    const reqId = ++quoteReqIdRef.current;\n    const input = buildInput({\n      fromToken,\n      toToken,\n      fromAmount,\n      toAmount,\n      lastChangedBy,\n      slippage,\n    });\n    setStatus((current) =>\n      current === \"submitting\"\n        ? current\n        : repository.canQuote\n          ? \"quoting\"\n          : \"idle\",\n    );\n    void (async () => {\n      try {\n        const result = await repository.getQuote(input);\n        if (quoteReqIdRef.current !== reqId) return;\n        if (result.fromAmount !== undefined && lastChangedBy === \"to\") {\n          setFromAmount(result.fromAmount);\n        }\n        if (result.toAmount !== undefined && lastChangedBy === \"from\") {\n          setToAmount(result.toAmount);\n        }\n        setSummary(result.summary ?? null);\n        setQuoteError(result.error ?? \"\");\n      } catch (err) {\n        if (quoteReqIdRef.current !== reqId) return;\n        setQuoteError(err instanceof Error ? err.message : \"Failed to quote bridge.\");\n      } finally {\n        if (quoteReqIdRef.current === reqId) {\n          setStatus((current) => (current === \"quoting\" ? \"idle\" : current));\n        }\n      }\n    })();\n  }, [\n    fromAmount,\n    fromToken,\n    lastChangedBy,\n    repository,\n    slippage,\n    toAmount,\n    toToken,\n  ]);\n\n  const getCurrentInput = React.useCallback(() => {\n    if (!fromToken || !toToken) return null;\n    return buildInput({\n      fromToken,\n      toToken,\n      fromAmount,\n      toAmount,\n      lastChangedBy,\n      slippage: effectiveSlippage,\n    });\n  }, [effectiveSlippage, fromAmount, fromToken, lastChangedBy, toAmount, toToken]);\n\n  const runSubmit = React.useCallback(async (\n    input: BridgeQuoteInput,\n    options?: { skipApprovalCheck?: boolean },\n  ) => {\n    if (submitLockRef.current) return;\n    submitLockRef.current = true;\n    setError(\"\");\n    setSuccessDialogOpen(false);\n    setApprovalSuccessOpen(false);\n    setStatus(\"submitting\");\n    try {\n      if (!options?.skipApprovalCheck && getApproval) {\n        const nextApproval = await getApproval(input);\n        if (nextApproval) {\n          setApprovalInfo(nextApproval);\n          setPendingSubmitInput(input);\n          setStatus(\"idle\");\n          return;\n        }\n      }\n      setApprovalInfo(null);\n      setPendingSubmitInput(null);\n      showSubmitToast(\"processing\");\n      const info = await repository.submitBridge(input);\n      setSubmitted(info);\n      setStatus(\"success\");\n      setLocalHistory((items) => [\n        {\n          id: info.txHash,\n          timestamp: new Date().toISOString(),\n          txHash: info.txHash,\n          fromToken: info.fromToken,\n          toToken: info.toToken,\n          fromAmount: info.fromAmount,\n          toAmount: info.toAmount,\n          summary: summary ?? undefined,\n          status: \"pending\",\n        },\n        ...items.filter((item) => item.txHash !== info.txHash),\n      ]);\n      resetInputAfterSubmit();\n      setHasNewHistory(true);\n      showSubmitToast(null);\n      setSuccessDialogOpen(true);\n      trackDappUiFunnel(\"bridge\", \"confirmed\");\n      funnelSubmittedRef.current = false;\n      funnelApprovedRef.current = false;\n      onSubmitted?.(info);\n      onSuccess?.(info);\n    } catch (err) {\n      showSubmitToast(null);\n      trackDappUiFunnel(\"bridge\", \"confirmed\", {\n        status: \"failure\",\n        reason: normalizeFailureReason(err),\n      });\n      funnelSubmittedRef.current = false;\n      funnelApprovedRef.current = false;\n      // A wallet cancellation isn't an error — return to idle silently.\n      if (isUserRejection(err)) {\n        setStatus(\"idle\");\n        return;\n      }\n      const message = bridgeErrorMessage(err, \"Failed to submit bridge.\");\n      setError(message);\n      setStatus(\"error\");\n      onFailed?.({ message });\n    } finally {\n      submitLockRef.current = false;\n    }\n  }, [\n    getApproval,\n    onFailed,\n    onSubmitted,\n    onSuccess,\n    repository,\n    resetInputAfterSubmit,\n    showSubmitToast,\n    summary,\n  ]);\n\n  const submit = React.useCallback(async () => {\n    if (quoteError) return;\n    const input = getCurrentInput();\n    if (!input) return;\n    if (!funnelSubmittedRef.current) {\n      funnelSubmittedRef.current = true;\n      trackDappUiFunnel(\"bridge\", \"submitted\");\n    }\n    if (!termsAccepted) {\n      setPendingSubmitInput(input);\n      setTermsOpen(true);\n      return;\n    }\n    await runSubmit(input);\n  }, [getCurrentInput, quoteError, runSubmit, termsAccepted]);\n\n  const confirmTerms = React.useCallback(async () => {\n    const input = pendingSubmitInput ?? getCurrentInput();\n    if (!input) return;\n    persistAcceptedTermsCookie();\n    setTermsAccepted(true);\n    setTermsOpen(false);\n    await runSubmit(input);\n  }, [getCurrentInput, pendingSubmitInput, runSubmit]);\n\n  const cancelTerms = React.useCallback(() => {\n    setTermsOpen(false);\n    setPendingSubmitInput(null);\n  }, []);\n\n  const confirmApproval = React.useCallback(async () => {\n    if (\n      approvalLockRef.current ||\n      status === \"submitting\" ||\n      !approvalInfo ||\n      !pendingSubmitInput ||\n      !approveBridge\n    ) {\n      return;\n    }\n    approvalLockRef.current = true;\n    setError(\"\");\n    setStatus(\"submitting\");\n    try {\n      await approveBridge(pendingSubmitInput, approvalInfo);\n      if (!funnelApprovedRef.current) {\n        funnelApprovedRef.current = true;\n        trackDappUiFunnel(\"bridge\", \"approved\");\n      }\n      setApprovalInfo(null);\n      setStatus(\"idle\");\n      setApprovalSuccessOpen(true);\n    } catch (err) {\n      // A wallet cancellation isn't an error — return to idle silently.\n      if (isUserRejection(err)) {\n        setStatus(\"idle\");\n        return;\n      }\n      const message = bridgeErrorMessage(err, \"Failed to approve token.\");\n      setError(message);\n      setStatus(\"error\");\n      onFailed?.({ message });\n    } finally {\n      approvalLockRef.current = false;\n    }\n  }, [\n    approvalInfo,\n    approveBridge,\n    onFailed,\n    pendingSubmitInput,\n    runSubmit,\n    status,\n  ]);\n\n  const cancelApproval = React.useCallback(() => {\n    if (status === \"submitting\") return;\n    setApprovalInfo(null);\n    setApprovalSuccessOpen(false);\n    setPendingSubmitInput(null);\n    setStatus(\"idle\");\n  }, [status]);\n\n  const continueAfterApproval = React.useCallback(async () => {\n    if (status === \"submitting\") return;\n    const input = pendingSubmitInput ?? getCurrentInput();\n    if (!input) {\n      setApprovalSuccessOpen(false);\n      setError(\"Bridge input is no longer available. Please try again.\");\n      setStatus(\"error\");\n      return;\n    }\n    setApprovalSuccessOpen(false);\n    await runSubmit(input, { skipApprovalCheck: true });\n  }, [getCurrentInput, pendingSubmitInput, runSubmit, status]);\n\n  const dismissSuccessDialog = React.useCallback(() => {\n    setSuccessDialogOpen(false);\n  }, []);\n\n  const setBridgeStep = React.useCallback((next: BridgeStep) => {\n    setStep(next);\n    if (next === \"history\") {\n      setHasNewHistory(false);\n    }\n  }, []);\n\n  const back = React.useCallback(() => {\n    if (step === \"history\") {\n      setBridgeStep(\"form\");\n      return;\n    }\n    onExit?.();\n  }, [onExit, setBridgeStep, step]);\n\n  const mergedHistory = React.useMemo(\n    () => [\n      ...history,\n      ...localHistory.filter(\n        (item) =>\n          !history.some((remoteItem) => remoteItem.txHash === item.txHash),\n      ),\n    ],\n    [history, localHistory],\n  );\n\n  return {\n    step,\n    setStep: setBridgeStep,\n    status,\n    error,\n    quoteError,\n    fromToken,\n    toToken,\n    tokens,\n    availableToTokens,\n    fromAmount,\n    toAmount,\n    lastChangedBy,\n    slippage,\n    effectiveSlippage,\n    slippageLocked,\n    setSlippage,\n    summary,\n    history: mergedHistory,\n    submitted,\n    canSubmit: repository.canSubmit,\n    termsOpen,\n    approvalInfo,\n    approvalSuccessOpen,\n    approveBridge,\n    pendingSubmitInput,\n    submitToast,\n    successDialogOpen,\n    dismissSuccessDialog,\n    hasNewHistory,\n    termsUrl,\n    setFromToken,\n    setToToken,\n    selectFromToken,\n    selectToToken,\n    setAmount,\n    setMax,\n    swapTokens,\n    submit,\n    confirmTerms,\n    cancelTerms,\n    confirmApproval,\n    cancelApproval,\n    continueAfterApproval,\n    back,\n  };\n}\n\nexport type BridgeFlow = ReturnType<typeof useBridgeFlow>;\n","import BigNumber from \"bignumber.js\";\n\nimport { parseNumeric } from \"../../../lib/numeric\";\nimport type {\n  BridgeQuoteFn,\n  BridgeQuoteInput,\n  BridgeQuoteResult,\n  BridgeSubmitFn,\n  BridgeSubmittedInfo,\n} from \"../types\";\n\nexport interface BridgeRepositoryDeps {\n  getQuote?: BridgeQuoteFn;\n  submitBridge?: BridgeSubmitFn;\n}\n\nexport interface BridgeRepository {\n  canQuote: boolean;\n  canSubmit: boolean;\n  getQuote(input: BridgeQuoteInput): Promise<BridgeQuoteResult>;\n  submitBridge(input: BridgeQuoteInput): Promise<BridgeSubmittedInfo>;\n}\n\nfunction isPositivePrice(value?: number): value is number {\n  return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\n\nfunction formatFallbackAmount(value: BigNumber): string {\n  if (!value.isFinite() || value.isNaN() || value.lte(0)) return \"\";\n  return value.decimalPlaces(6, BigNumber.ROUND_DOWN).toFixed();\n}\n\nfunction canUseOneToOneFallback(input: BridgeQuoteInput): boolean {\n  return (\n    input.fromToken.symbol.toLowerCase() === input.toToken.symbol.toLowerCase() ||\n    (input.fromToken.chainId === input.toToken.chainId &&\n      input.fromToken.address.toLowerCase() === input.toToken.address.toLowerCase())\n  );\n}\n\nfunction getFallbackQuote(input: BridgeQuoteInput): BridgeQuoteResult {\n  const fromPrice = input.fromToken.priceUsd;\n  const toPrice = input.toToken.priceUsd;\n  const sourceAmount =\n    input.lastChangedBy === \"to\" ? input.toAmount : input.fromAmount;\n  // `sanitizeAmountInput` 은 \".\" 하나만 남은 입력도 통과시키는데,\n  // `new BigNumber(\".\")` 는 NaN 이 아니라 예외를 던진다 — 소수점만 찍는 순간\n  // 견적 계산이 죽었다.\n  const amount = parseNumeric(sourceAmount);\n\n  if (!amount || amount.lte(0)) {\n    return {\n      fromAmount: input.fromAmount,\n      toAmount: input.toAmount,\n    };\n  }\n\n  if (isPositivePrice(fromPrice) && isPositivePrice(toPrice)) {\n    if (input.lastChangedBy === \"to\") {\n      return {\n        fromAmount: formatFallbackAmount(\n          amount.multipliedBy(toPrice).dividedBy(fromPrice),\n        ),\n      };\n    }\n\n    return {\n      toAmount: formatFallbackAmount(\n        amount.multipliedBy(fromPrice).dividedBy(toPrice),\n      ),\n    };\n  }\n\n  if (!canUseOneToOneFallback(input)) {\n    return {\n      fromAmount: input.lastChangedBy === \"to\" ? \"\" : input.fromAmount,\n      toAmount: input.lastChangedBy === \"from\" ? \"\" : input.toAmount,\n    };\n  }\n\n  return input.lastChangedBy === \"to\"\n    ? { fromAmount: input.toAmount }\n    : { toAmount: input.fromAmount };\n}\n\nexport function createBridgeRepository({\n  getQuote,\n  submitBridge,\n}: BridgeRepositoryDeps): BridgeRepository {\n  return {\n    canQuote: Boolean(getQuote),\n    canSubmit: Boolean(submitBridge),\n\n    async getQuote(input) {\n      if (!getQuote) {\n        return getFallbackQuote(input);\n      }\n      return getQuote(input);\n    },\n\n    async submitBridge(input) {\n      if (!submitBridge) {\n        throw new Error(\"Bridge submit handler is not configured.\");\n      }\n      return submitBridge(input);\n    },\n  };\n}\n","import styleInject from '#style-inject';styleInject(\".bf-root{--bf-bg: var(--ds-bg-default, #1e232e);--bf-surface: var(--ds-surface-medium, rgba(255, 255, 255, .05));--bf-surface-hover: var(--ds-surface-high, rgba(255, 255, 255, .08));--bf-border: var(--ds-border-default, rgba(255, 255, 255, .07));--bf-text-high: var(--ds-content-high, rgba(255, 255, 255, .85));--bf-text-medium: var(--ds-content-medium, rgba(255, 255, 255, .65));--bf-text-low: var(--ds-content-low, rgba(255, 255, 255, .4));--bf-toast-bg: var(--bf-bg);--bf-toast-strong: var(--bf-text-high);--bf-toast-color: var(--bf-text-medium);position:relative;display:flex;flex:1;min-height:0;flex-direction:column;container:bf-flow / inline-size;overflow:hidden;background:var(--bf-bg);color:var(--ds-content-highest, #ffffff);font-family:inherit}[data-theme=dark].wi-popover-content .bf-root,[data-theme=dark].wi-drawer-content .bf-root{--bf-bg: var(--ds-bg-default, #121212);--bf-surface: var(--ds-surface-medium, rgba(255, 255, 255, .05));--bf-surface-hover: var(--ds-surface-high, rgba(255, 255, 255, .1));--bf-border: var(--ds-border-default, rgba(255, 255, 255, .05));--bf-text-high: var(--ds-content-high, rgba(255, 255, 255, .85));--bf-text-medium: var(--ds-content-medium, rgba(255, 255, 255, .7));--bf-text-low: var(--ds-content-low, rgba(255, 255, 255, .5))}[data-theme=light].wi-popover-content .bf-root,[data-theme=light].wi-drawer-content .bf-root{--bf-bg: var(--ds-bg-default, #ffffff);--bf-surface: var(--ds-surface-medium, rgba(18, 18, 18, .05));--bf-surface-hover: var(--ds-surface-high, rgba(18, 18, 18, .1));--bf-border: var(--ds-border-default, rgba(18, 18, 18, .05));--bf-text-high: var(--ds-content-high, rgba(18, 18, 18, .85));--bf-text-medium: var(--ds-content-medium, rgba(18, 18, 18, .7));--bf-text-low: var(--ds-content-low, rgba(18, 18, 18, .5))}[data-theme=dark].gou-shell .bf-root,.gou-shell[data-theme=dark] .bf-root{--bf-bg: var(--ds-bg-default, #121212);--bf-surface: var(--ds-surface-medium, rgba(255, 255, 255, .05));--bf-surface-hover: var(--ds-surface-high, rgba(255, 255, 255, .1));--bf-border: var(--ds-border-default, rgba(255, 255, 255, .05));--bf-text-high: var(--ds-content-high, rgba(255, 255, 255, .85));--bf-text-medium: var(--ds-content-medium, rgba(255, 255, 255, .7));--bf-text-low: var(--ds-content-low, rgba(255, 255, 255, .5))}[data-theme=light].gou-shell .bf-root,.gou-shell[data-theme=light] .bf-root{--bf-bg: var(--ds-bg-default, #ffffff);--bf-surface: var(--ds-surface-medium, rgba(18, 18, 18, .05));--bf-surface-hover: var(--ds-surface-high, rgba(18, 18, 18, .1));--bf-border: var(--ds-border-default, rgba(18, 18, 18, .05));--bf-text-high: var(--ds-content-high, rgba(18, 18, 18, .85));--bf-text-medium: var(--ds-content-medium, rgba(18, 18, 18, .7));--bf-text-low: var(--ds-content-low, rgba(18, 18, 18, .5))}.bf-root[data-view=history]{background:var(--ds-bg-dim, #f5f7fa);color:var(--ds-content-high, rgba(0, 0, 0, .85))}.bf-header{position:relative;display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:24px;border-bottom:1px solid var(--bf-border);background:var(--ds-bg-default, var(--bf-bg));box-sizing:border-box}.bf-root[data-view=history] .bf-header{padding:24px;border-bottom-color:var(--ds-border-default, rgba(0, 0, 0, .07));background:var(--ds-surface-default, #ffffff)}.bf-header-btn{display:inline-flex;width:24px;height:24px;flex-shrink:0;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer;transition:color .15s ease}.bf-header-spacer{width:24px;height:24px;flex-shrink:0}.bf-header-btn:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.bf-header .bf-header-btn:last-child{position:absolute;top:24px;right:24px}.bf-header-title{position:absolute;left:50%;transform:translate(-50%);font-size:18px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--bf-text-high);white-space:nowrap}.bf-header-title[data-clickable=true]{cursor:pointer}.bf-root[data-view=history] .bf-header-title{color:var(--ds-content-highest, #000000)}.bf-body,.bf-history-body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;box-sizing:border-box}.bf-body{padding:24px}.bf-history-body{display:flex;flex-direction:column;gap:12px;padding:24px;background:var(--ds-bg-dim, #f5f7fa)}.bf-toolbar{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px}.bf-chip{display:inline-flex;align-items:center;gap:4px;min-height:33px;padding:6px 8px;border:0;border-radius:12px;background:var(--bf-surface);color:var(--bf-text-medium);font:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer}.bf-chip:hover:not(:disabled){color:var(--bf-text-high);background:var(--bf-surface-hover)}.bf-chip:disabled{cursor:default}.bf-chip-history{position:relative}.bf-chip-dot{position:absolute;top:4px;right:4px;width:6px;height:6px;border-radius:999px;background:var(--ds-system-red-default, #da0b2e)}.bf-swap-card{position:relative;display:flex;flex-direction:column;gap:12px;margin-bottom:12px}.bf-field-error{margin-top:-4px;color:var(--ds-system-red-default, #ff2b5f);font-size:13px;font-weight:500;line-height:1.5;letter-spacing:0}.bf-amount-panel{display:flex;min-height:110px;flex-direction:column;gap:8px;padding:16px;border:1px solid transparent;border-radius:12px;background:var(--bf-surface);box-sizing:border-box;transition:border-color .2s ease}.bf-amount-panel:hover{border-color:var(--bf-border)}.bf-amount-head{display:flex;align-items:center;justify-content:space-between;gap:8px;color:var(--bf-text-high);font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-balance{display:inline-flex;align-items:center;justify-content:flex-end;gap:4px;min-width:0;color:var(--bf-text-low);font-size:12px;white-space:nowrap}.bf-amount-main{display:flex;align-items:center;justify-content:space-between;gap:12px}.bf-amount-input-wrap{display:flex;min-width:0;flex:1;flex-direction:column;gap:4px}.bf-amount-input{width:100%;min-width:0;padding:0;border:0;background:transparent;color:var(--bf-text-high);font:inherit;font-size:18px;font-size:clamp(15px,4.5cqi,18px);font-weight:600;line-height:1.5;letter-spacing:-.01em;outline:none}.bf-amount-input[data-size=medium]{font-size:16px;font-size:clamp(14px,4cqi,16px)}.bf-amount-input[data-size=small]{font-size:14px;font-size:clamp(12px,3.5cqi,14px)}.bf-amount-input::placeholder{color:var(--ds-content-lowest, rgba(0, 0, 0, .25))}.bf-amount-usd{overflow:hidden;color:var(--bf-text-low);font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;white-space:nowrap;text-overflow:ellipsis}.bf-token-pill{display:inline-flex;flex-shrink:0;align-items:center;gap:6px;min-height:33px;padding:6px 8px 6px 6px;border:1px solid var(--bf-border);border-radius:9999px;background:var(--bf-bg);color:var(--bf-text-high);font:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer;perspective:220px;overflow:hidden;transition:transform .12s ease,background .15s ease}.bf-token-pill:hover:not(:disabled):not([data-locked=true]){background:var(--bf-surface-hover)}.bf-token-pill:disabled{cursor:default;opacity:.6}.bf-token-pill[data-locked=true]{cursor:default}.bf-token-icon{position:relative;display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;overflow:hidden;border-radius:0;background:var(--ds-accent-primary-default, #f0640d);color:var(--ds-accent-primary-on, #000000);font-size:11px;font-weight:700}.bf-token-icon[data-has-image=true]{background:transparent}.bf-token-icon img{width:100%;height:100%;border-radius:0;object-fit:cover}.bf-token-fallback{line-height:1}.bf-token-pill-face{display:inline-flex;align-items:center;gap:6px;transform-origin:center bottom;animation:bf-token-roll-up .32s cubic-bezier(.22,1,.36,1)}@keyframes bf-token-roll-up{0%{opacity:0;transform:translateY(90%) rotateX(-80deg)}60%{opacity:1}}.bf-token-pill:active:not(:disabled),.bf-embedded-token-pill:active:not(:disabled){transform:scale(.96)}.bf-token-search:focus-within{border-color:var(--ds-accent-primary-default, #f0640d)}.bf-token-search input{width:100%;min-width:0;padding:0;border:0;outline:0;background:transparent;color:var(--bf-text-high);font:inherit;font-size:16px;font-weight:500;line-height:1.5;letter-spacing:0}.bf-token-search input::placeholder{color:var(--bf-text-low)}.bf-token-list{display:flex;min-height:0;flex:1;flex-direction:column;gap:0;overflow-y:auto;overflow-x:hidden;padding:0 24px 24px;scrollbar-width:thin;box-sizing:border-box}.bf-token-row{position:relative;display:flex;width:100%;min-height:69px;align-items:center;justify-content:space-between;gap:12px;padding:12px;border:1px solid transparent;border-radius:12px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer;box-sizing:border-box}.bf-token-row:hover:not([data-selected=true]){background:var(--ds-surface-medium, rgba(18, 18, 18, .05))}.bf-token-row[data-selected=true]{border-color:var(--ds-accent-primary-default, #ff8438);background:var(--ds-surface-medium, rgba(18, 18, 18, .05))}.bf-token-row-left{display:inline-flex;min-width:0;align-items:center;gap:16px}.bf-token-meta{display:flex;min-width:0;flex-direction:column}.bf-token-symbol{overflow:hidden;color:var(--bf-text-high);font-size:16px;font-weight:600;line-height:1.5;letter-spacing:0;text-overflow:ellipsis;white-space:nowrap}.bf-token-name{overflow:hidden;color:var(--bf-text-low);font-size:12px;font-weight:500;line-height:1.5;letter-spacing:0;text-overflow:ellipsis;white-space:nowrap}.bf-token-row-right{display:flex;min-width:86px;max-width:42%;flex-shrink:0;flex-direction:column;align-items:flex-end}.bf-token-balance,.bf-token-price{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bf-token-balance{color:var(--bf-text-high);font-size:14px;font-weight:600;line-height:1.5;letter-spacing:0}.bf-token-price{color:var(--bf-text-low);font-size:12px;font-weight:500;line-height:1.5;letter-spacing:0}.bf-token-row[data-selected=true] .bf-token-symbol{color:var(--bf-text-high)}.bf-token-row[data-selected=true] .bf-token-balance{color:var(--bf-text-high);font-size:16px}.bf-token-row[data-selected=true] .bf-token-price{color:var(--bf-text-low);font-size:14px}.bf-token-empty{padding:32px 16px;color:var(--bf-text-low);font-size:14px;font-weight:500;line-height:1.5;text-align:center}.bf-slippage-overlay{position:fixed;inset:0;z-index:100002;display:flex;align-items:center;justify-content:center;padding:24px;background:#00000085;pointer-events:auto;box-sizing:border-box}.bf-slippage-dialog{display:flex;width:min(480px,calc(100vw - 48px));height:auto;min-height:min(316px,calc(100dvh - 48px));max-height:calc(100dvh - 48px);flex-direction:column;overflow:hidden;border:1px solid var(--ds-border-medium, rgba(0, 0, 0, .18));border-radius:24px;background:var(--ds-surface-default, #ffffff);color:var(--ds-content-highest, #000000);box-shadow:var(--ds-shadow-modal, 0 4px 30px -1px rgba(0, 0, 0, .15));box-sizing:border-box}.bf-slippage-head{position:relative;display:flex;min-height:73px;flex-shrink:0;align-items:center;justify-content:flex-start;padding:24px 56px 24px 24px;border-bottom:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));background:var(--ds-surface-default, #ffffff);box-sizing:border-box}.bf-slippage-head h3{margin:0;color:var(--ds-content-highest, #000000);font-size:20px;font-weight:600;line-height:1.25;letter-spacing:0}.bf-slippage-close{position:absolute;top:24px;right:24px;display:inline-flex;width:24px;height:24px;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer}.bf-slippage-close:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.bf-slippage-body{display:flex;flex:1;min-height:0;flex-direction:column;gap:24px;padding:24px;box-sizing:border-box}.bf-slippage-desc{margin:0;color:var(--ds-content-medium, rgba(0, 0, 0, .65));font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-slippage-controls{display:flex;flex-direction:column;gap:12px;width:100%}.bf-slippage-presets{display:flex;gap:8px;width:100%}.bf-slippage-preset{appearance:none;display:inline-flex;flex:1 1 0;min-width:0;min-height:48px;align-items:center;justify-content:center;padding:12px 8px;border:1px solid transparent;border-radius:12px;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-content-medium, rgba(0, 0, 0, .65));font:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer;box-sizing:border-box}.bf-slippage-preset:hover,.bf-slippage-preset[data-selected=true]{border-color:var(--ds-accent-primary-default, #ff8438);background:var(--ds-accent-primary-low, #fff5ee)}.bf-slippage-custom{display:flex;min-height:48px;align-items:center;gap:8px;padding:12px;border:1px solid transparent;border-radius:12px;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-content-high, rgba(0, 0, 0, .85));box-sizing:border-box}.bf-slippage-custom:focus-within,.bf-slippage-custom[data-selected=true]{border-color:var(--ds-accent-primary-default, #ff8438)}.bf-slippage-custom input{width:100%;min-width:0;padding:0;border:0;outline:0;background:transparent;color:var(--ds-content-high, rgba(0, 0, 0, .85));font:inherit;font-size:16px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-slippage-custom input::placeholder{color:var(--ds-content-lowest, rgba(0, 0, 0, .25))}.bf-slippage-custom span:not(.bf-sr-only){flex-shrink:0;color:var(--ds-content-high, rgba(0, 0, 0, .85));font-size:16px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-slippage-apply{appearance:none;margin-top:auto;width:100%;min-height:48px;padding:12px;border:0;border-radius:12px;background:var(--ds-accent-primary-default, #ff8438);color:var(--ds-accent-primary-on-primary, #ffffff);font:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer;box-sizing:border-box}.bf-slippage-apply:hover{filter:brightness(.96)}.bf-slippage-apply:disabled{cursor:not-allowed;opacity:.5;filter:none}.bf-slippage-message{margin:0;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-slippage-message[data-variant=warning]{color:var(--ds-system-orange-high, #e56f00)}.bf-slippage-message[data-variant=error]{color:var(--ds-system-red-default, #da0b2e)}.bf-action-modal-overlay{--bf-bg: var(--ds-bg-default, #1e232e);--bf-surface: var(--ds-surface-medium, rgba(255, 255, 255, .05));--bf-surface-hover: var(--ds-surface-high, rgba(255, 255, 255, .08));--bf-border: var(--ds-border-default, rgba(255, 255, 255, .12));--bf-text-high: var(--ds-content-high, rgba(255, 255, 255, .92));--bf-text-medium: var(--ds-content-medium, rgba(255, 255, 255, .65));--bf-text-low: var(--ds-content-low, rgba(255, 255, 255, .4));position:fixed;inset:0;z-index:100003;display:flex;align-items:center;justify-content:center;padding:24px;background:#040912b8;pointer-events:auto;box-sizing:border-box;animation:bf-overlay-in .18s ease-out}.bf-action-modal-overlay[data-theme=light]{--bf-bg: var(--ds-bg-default, #ffffff);--bf-surface: var(--ds-surface-medium, rgba(18, 18, 18, .05));--bf-surface-hover: var(--ds-surface-high, rgba(18, 18, 18, .1));--bf-border: var(--ds-border-default, rgba(18, 18, 18, .08));--bf-text-high: var(--ds-content-high, rgba(18, 18, 18, .85));--bf-text-medium: var(--ds-content-medium, rgba(18, 18, 18, .65));--bf-text-low: var(--ds-content-low, rgba(18, 18, 18, .4))}.bf-action-modal{display:flex;width:min(480px,calc(100vw - 48px));flex-direction:column;align-items:center;gap:24px;padding:48px 24px 24px;border:1px solid var(--bf-border);border-radius:24px;background:var(--bf-bg);color:#fff;box-shadow:0 24px 80px #00000073;box-sizing:border-box;animation:bf-modal-in .2s cubic-bezier(.16,1,.3,1)}.bf-action-modal-overlay[data-state=closed]{animation:bf-overlay-out .15s ease-in forwards}.bf-action-modal-overlay[data-state=closed] .bf-action-modal{animation:bf-modal-out .15s ease-in forwards}@keyframes bf-overlay-in{0%{opacity:0}}@keyframes bf-overlay-out{to{opacity:0}}@keyframes bf-modal-in{0%{opacity:0;transform:translateY(8px) scale(.96)}}@keyframes bf-modal-out{to{opacity:0;transform:translateY(4px) scale(.98)}}@media(prefers-reduced-motion:reduce){.bf-action-modal-overlay,.bf-action-modal,.bf-action-modal-overlay[data-state=closed],.bf-action-modal-overlay[data-state=closed] .bf-action-modal{animation:none}}.bf-action-modal h3{width:100%;margin:0;color:var(--bf-text-high);font-size:24px;font-weight:700;line-height:1.25;text-align:left;letter-spacing:0}.bf-action-modal-desc{max-width:380px;margin:0;color:var(--bf-text-low);font-size:15px;font-weight:500;line-height:1.45;text-align:center;letter-spacing:0}.bf-terms-modal{--bf-terms-accent: #f3742e;padding:24px;gap:24px}.bf-terms-modal h3{font-size:20px;font-weight:600;line-height:1.6;text-align:center}.bf-terms-modal .bf-action-modal-desc{font-size:14px;line-height:1.45}.bf-terms-check-row{display:grid;width:100%;min-height:56px;grid-template-columns:20px minmax(0,1fr);align-items:center;column-gap:12px;padding:16px;border:1px solid var(--bf-border);border-radius:12px;background:var(--bf-surface);color:var(--bf-text-high);font-size:14px;font-weight:600;line-height:1.4;box-sizing:border-box}.bf-terms-check-button{appearance:none;display:inline-flex;min-width:0;width:20px;height:20px;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer;touch-action:manipulation}.bf-terms-copy{display:flex;width:100%;min-width:0;align-items:baseline;justify-content:space-between;column-gap:12px;row-gap:4px;flex-wrap:wrap}.bf-terms-label{min-width:0;cursor:pointer;touch-action:manipulation}.bf-terms-check-button:focus-visible .bf-terms-checkbox{outline:2px solid var(--ds-accent-primary-default, #f0640d);outline-offset:2px}.bf-terms-checkbox{position:relative;display:inline-flex;width:20px;height:20px;align-items:center;justify-content:center;border:1px solid rgba(124,143,180,.72);border-radius:4px;background:transparent;box-sizing:border-box}.bf-terms-checkbox[data-checked=true]{border-color:var(--ds-accent-primary-default, #f0640d);background:var(--ds-accent-primary-default, #f0640d)}.bf-terms-checkbox[data-checked=true]:after{width:8px;height:4px;border:solid #ffffff;border-width:0 0 2px 2px;content:\\\"\\\";transform:rotate(-45deg) translate(1px,-1px)}.bf-terms-check-row a{position:relative;z-index:1;color:var(--ds-accent-primary-default, #f0640d);font-size:14px;font-weight:700;text-decoration:none;white-space:nowrap}.bf-terms-check-row a:hover{text-decoration:underline}.bf-terms-privacy{max-width:100%;margin:0;color:var(--bf-text-low);font-size:clamp(8px,2.4vw,12px);white-space:nowrap;font-weight:500;line-height:1.45;text-align:center;letter-spacing:0}.bf-action-primary,.bf-action-secondary{display:inline-flex;width:100%;min-height:56px;align-items:center;justify-content:center;padding:14px 16px;border:0;border-radius:12px;font:inherit;font-size:18px;font-weight:700;line-height:1.4;letter-spacing:0;cursor:pointer}.bf-action-primary{background:var(--ds-accent-primary-default, #f0640d);color:var(--ds-accent-primary-on, #000000)}.bf-action-primary:disabled{cursor:not-allowed;opacity:.35}.bf-terms-modal .bf-action-primary{min-height:52px;background:var(--bf-terms-accent);color:#fff;font-size:16px;font-weight:600}.bf-terms-modal .bf-action-primary:disabled{background:var(--bf-surface);color:var(--bf-text-low);opacity:1}.bf-action-primary[aria-busy=true]{gap:8px;opacity:1}.bf-action-button-spinner{width:18px;height:18px;border:2px solid rgba(0,0,0,.22);border-top-color:currentColor;border-right-color:currentColor;border-radius:999px;animation:bf-loading-spin .9s linear infinite}.bf-action-secondary{background:var(--bf-surface);color:var(--bf-text-low)}.bf-action-secondary:disabled{cursor:not-allowed;opacity:.5}.bf-approve-address-box{display:flex;width:100%;flex-direction:column;gap:10px;padding:16px;border:1px solid var(--bf-border);border-radius:12px;background:var(--bf-surface);box-sizing:border-box}.bf-approve-address-box>span{color:var(--bf-text-low);font-size:14px;font-weight:700;line-height:1.4}.bf-approve-address-box>div{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}.bf-approve-address-box strong{min-width:0;overflow:hidden;color:var(--bf-text-high);font-size:15px;font-weight:500;line-height:1.5;text-overflow:ellipsis;white-space:nowrap}.bf-copy-button{flex-shrink:0;min-width:48px;min-height:32px;padding:6px 10px;border:1px solid rgba(0,206,178,.65);border-radius:8px;background:transparent;color:var(--ds-accent-primary-default, #f0640d);font:inherit;font-size:12px;font-weight:700;line-height:1.2;cursor:pointer}.bf-copy-button:disabled{cursor:not-allowed;opacity:.45}.bf-action-buttons{display:grid;width:100%;grid-template-columns:1fr 1fr;gap:12px}.bf-approval-success-modal{gap:28px}.bf-success-mark{position:relative;display:inline-flex;width:96px;height:96px;flex-shrink:0;align-items:center;justify-content:center;border:12px solid rgba(0,206,178,.12);border-radius:999px;background:#00ceb2a8;box-shadow:0 0 0 10px #00ceb20f;box-sizing:border-box}.bf-success-mark:after{width:34px;height:18px;border:solid #ffffff;border-width:0 0 6px 6px;content:\\\"\\\";transform:rotate(-45deg) translate(2px,-3px)}.bf-status-toast{position:absolute;right:16px;bottom:14px;left:16px;z-index:5;display:flex;align-items:center;gap:12px;min-height:52px;padding:12px;border:1px solid var(--bf-border);border-radius:12px;background:var(--bf-toast-bg);color:var(--bf-toast-color);box-shadow:0 8px 24px #0000003d;box-sizing:border-box}.bf-status-toast-icon{display:inline-flex;width:28px;height:28px;flex-shrink:0;align-items:center;justify-content:center;border-radius:999px}.bf-status-spinner{width:20px;height:20px;border:3px solid rgba(255,255,255,.18);border-top-color:var(--ds-accent-primary-default, #f0640d);border-right-color:var(--ds-accent-primary-default, #f0640d);border-radius:999px;animation:bf-loading-spin .9s linear infinite}.bf-status-check{position:relative;width:20px;height:20px;border-radius:999px;background:var(--ds-system-green-default, #00ceb2)}.bf-status-check:after{position:absolute;top:6px;left:5px;width:8px;height:4px;border:solid var(--ds-system-green-on, #000000);border-width:0 0 2px 2px;content:\\\"\\\";transform:rotate(-45deg)}.bf-status-toast-copy{display:flex;min-width:0;flex:1;flex-direction:column}.bf-status-toast-copy strong,.bf-status-toast-copy span{overflow:hidden;text-overflow:ellipsis}.bf-status-toast-copy strong{color:var(--bf-toast-strong);font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em}.bf-status-toast-copy span{color:var(--bf-toast-color);font-size:12px;font-weight:400;line-height:1.5;letter-spacing:-.01em}.bf-status-toast[data-variant=success] .bf-status-toast-copy span{color:var(--ds-system-green-on-inverted, #00e8c9)}.bf-status-toast[data-variant=error]{border:1px solid rgba(255,43,95,.28);background:var(--bf-toast-bg)}.bf-status-toast[data-variant=error] .bf-status-toast-copy strong{color:var(--bf-toast-strong)}.bf-status-error-mark{display:inline-flex;width:24px;height:24px;align-items:center;justify-content:center;border-radius:999px;background:var(--ds-system-red-default, #ff2b5f);color:#fff;font-size:14px;font-weight:700;line-height:1}.bf-status-toast[data-variant=error] .bf-status-toast-copy span{color:var(--bf-toast-color)}.bf-success-overlay{--bf-bg: var(--ds-bg-default, #1e232e);--bf-border: var(--ds-border-default, rgba(255, 255, 255, .12));--bf-text-high: var(--ds-content-high, rgba(255, 255, 255, .92));--bf-text-medium: var(--ds-content-medium, rgba(255, 255, 255, .65));position:fixed;inset:0;z-index:100003;display:flex;align-items:center;justify-content:center;padding:24px;background:#000000b8;pointer-events:auto;box-sizing:border-box}.bf-success-overlay[data-theme=light]{--bf-bg: #ffffff;--bf-border: rgba(18, 18, 18, .08);--bf-text-high: rgba(18, 18, 18, .85);--bf-text-medium: rgba(18, 18, 18, .65);--bf-text-low: rgba(18, 18, 18, .4)}.bf-success-modal{display:flex;width:min(480px,calc(100vw - 48px));flex-direction:column;overflow:hidden;border:1px solid var(--bf-border);border-radius:24px;background:var(--bf-bg);color:var(--bf-text-high);box-shadow:0 24px 80px #00000073;box-sizing:border-box}.bf-success-head{display:flex;min-height:73px;align-items:center;justify-content:space-between;gap:16px;padding:24px;border-bottom:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));box-sizing:border-box}.bf-success-head h3{min-width:0;margin:0;overflow:hidden;color:var(--ds-content-highest, #000000);font-size:20px;font-weight:600;line-height:1.25;text-overflow:ellipsis;white-space:nowrap}.bf-success-close{display:inline-flex;width:24px;height:24px;flex-shrink:0;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer}.bf-success-close:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.bf-success-body{display:flex;flex-direction:column;align-items:center;gap:24px;padding:40px 24px 24px;box-sizing:border-box}.bf-success-copy{display:flex;width:100%;flex-direction:column;align-items:center;gap:4px;color:var(--bf-text-medium);text-align:center}.bf-success-copy strong{color:var(--bf-text-high);font-size:24px;font-weight:700;line-height:1.25;letter-spacing:0}.bf-success-copy span{color:var(--bf-text-medium);font-size:15px;font-weight:500;line-height:1.5;letter-spacing:0}.bf-success-route{display:flex;align-items:center;justify-content:center;gap:12px}.bf-success-route .bf-token-icon{width:48px!important;height:48px!important;background:transparent;font-size:18px}.bf-success-route-arrow{color:var(--bf-text-low);font-size:24px;font-weight:500;line-height:1}.bf-success-ok{display:inline-flex;width:100%;min-height:48px;align-items:center;justify-content:center;padding:12px;border:0;border-radius:12px;background:#00b4b7e0;color:#fff;font:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer}@media(max-width:767px){.bf-success-body{padding:40px 24px 24px}.bf-success-copy strong{font-size:22px}.bf-success-copy span{font-size:14px}.bf-success-mark{width:88px;height:88px}.bf-success-route .bf-token-icon{width:44px!important;height:44px!important}}@keyframes bf-loading-spin{to{transform:rotate(360deg)}}.bf-swap-toggle{position:absolute;top:100px;left:50%;z-index:1;display:inline-flex;width:32px;height:32px;align-items:center;justify-content:center;padding:0;border:0;border-radius:9999px;background:var(--ds-accent-primary-default, #f0640d);color:var(--ds-accent-primary-on, #000000);box-shadow:0 1px 5px #0000001f;cursor:pointer}.bf-swap-card[data-has-from-error=true] .bf-swap-toggle{top:132px}.bf-details{display:flex;flex-direction:column;gap:24px}.bf-collapse{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .22s ease,opacity .14s ease,margin-top .22s ease}.bf-collapse[data-open=true]{grid-template-rows:1fr;opacity:1}.bf-collapse:not([data-open=true]){margin-top:calc(-1 * var(--bf-collapse-gap, 0px))}.bf-collapse-inner{overflow:hidden;min-height:0}@keyframes bf-value-in{0%{opacity:.25;transform:translateY(-2px)}}.bf-info-value,.bf-route-amount-value,.bf-route-meta>span{animation:bf-value-in .24s ease-out}@media(prefers-reduced-motion:reduce){.bf-collapse{transition:none}.bf-info-value,.bf-route-amount-value,.bf-route-meta>span{animation:none}}.bf-exchange-rate{display:flex;width:100%;align-items:center;justify-content:center;gap:4px;padding:0 0 24px;color:var(--bf-text-low);font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-detail-section,.bf-history-card{display:flex;flex-direction:column;gap:12px;padding:0;border-radius:12px;background:transparent;box-sizing:border-box}.bf-liquidity{display:flex;flex-direction:column;gap:12px;padding:16px;border-radius:12px;background:var(--bf-surface);box-sizing:border-box}.bf-root[data-view=history] .bf-liquidity,.bf-root[data-view=history] .bf-detail-section,.bf-root[data-view=history] .bf-history-card{background:var(--ds-surface-default, #ffffff)}.bf-detail-section h3{margin:0;color:var(--bf-text-high);font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em}.bf-tx-fee-section{gap:12px}.bf-tx-fee-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.bf-collapse-head{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;margin:0;padding:0;background:none;border:none;color:inherit;font:inherit;text-align:left;cursor:pointer}.bf-detail-section[data-open=true] .bf-collapse-head{padding-bottom:12px;border-bottom:1px solid var(--bf-border)}.bf-collapse-chevron{flex-shrink:0;color:var(--bf-text-low);transition:transform .2s ease}.bf-detail-section[data-open=true] .bf-collapse-chevron{transform:rotate(180deg)}.bf-collapse-body{display:flex;flex-direction:column;gap:14px}.bf-root[data-view=history] .bf-detail-section[data-open=true] .bf-collapse-head{border-bottom-color:var(--ds-border-default, rgba(0, 0, 0, .07))}.bf-root[data-view=history] .bf-collapse-chevron{color:var(--ds-content-low, rgba(0, 0, 0, .4))}.bf-tx-fee-total{display:inline-flex;align-items:center;justify-content:flex-end;gap:8px;min-width:0;color:var(--bf-text-medium);font-size:14px;font-weight:600;line-height:1.4;text-align:right}.bf-tx-fee-total>span[data-delegated=true]{text-decoration:line-through;text-decoration-thickness:2px;text-decoration-color:currentColor}.bf-fee-delegated{display:inline-flex;align-items:center;justify-content:center;min-height:24px;padding:2px 8px;border-radius:6px;background:#379cff2e;color:#4eb3ff;font-size:10px;font-weight:500;line-height:1.3;white-space:nowrap}.bf-tx-fee-token{width:16px;height:16px;flex-shrink:0;border-radius:999px;object-fit:cover}.bf-info-card{gap:14px}.bf-info-section{display:flex;flex-direction:column;gap:10px}.bf-info-section+.bf-info-section{padding-top:2px}.bf-info-section h4{margin:0;color:var(--bf-text-high);font-size:14px;font-weight:600;line-height:1.5}.bf-root[data-view=history] .bf-detail-section h3,.bf-root[data-view=history] .bf-info-section h4{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.bf-route-steps{display:flex;flex-direction:column}.bf-route-step{display:grid;min-width:0;grid-template-columns:18px minmax(0,1fr);column-gap:10px}.bf-route-rail{position:relative;display:flex;justify-content:center}.bf-route-line{position:absolute;top:9px;bottom:-4px;width:1px;background:var(--bf-border)}.bf-route-step[data-last=true] .bf-route-line{display:none}.bf-route-dot{z-index:1;width:9px;height:9px;margin-top:5px;border:2px solid var(--bf-text-low);border-radius:999px;background:var(--bf-bg);box-sizing:border-box}.bf-route-dot[data-final=true]{border-color:var(--ds-accent-primary-default, #00ceb2)}.bf-route-body{display:flex;min-width:0;flex-direction:column;gap:2px;padding-bottom:14px}.bf-route-step[data-last=true] .bf-route-body{padding-bottom:0}.bf-route-head{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}.bf-route-title{min-width:0;overflow:hidden;color:var(--bf-text-high);font-size:13px;font-weight:620;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.bf-route-amount{display:inline-flex;flex-shrink:0;align-items:center;gap:4px;color:var(--bf-text-medium);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}.bf-route-meta{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;color:var(--bf-text-low);font-size:12px;line-height:1.45;font-variant-numeric:tabular-nums}.bf-route-step[data-kind=hop] .bf-route-title{color:var(--bf-text-medium);font-weight:600}.bf-route-step[data-kind=hop] .bf-route-amount{color:var(--bf-text-low);font-weight:500}.bf-route-step[data-kind=hop] .bf-route-dot{width:7px;height:7px;margin-top:6px;border-width:1px}.bf-route-meta>span[data-tone=accent]{color:var(--ds-accent-primary-default, #00ceb2);font-weight:600}.bf-route-meta>span[data-tone=warning]{color:var(--ds-system-orange-high, #e56f00);font-weight:600}.bf-route-meta>span[data-tone=danger]{color:var(--ds-system-red-default, #da0b2e);font-weight:600}.bf-route-tag{padding:1px 6px;border-radius:5px;background:var(--ds-accent-primary-low, rgba(0, 206, 178, .12));color:var(--ds-accent-primary-high, #00a0a2);font-size:10px;font-weight:700;letter-spacing:.01em}.bf-info-rows{display:flex;flex-direction:column;gap:8px}.bf-info-row{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-liquidity-head{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em}.bf-info-label{display:inline-flex;align-items:center;gap:6px;min-width:0;color:var(--bf-text-low)}.bf-info-value{display:flex;min-width:0;align-items:center;justify-content:flex-end;gap:4px;color:var(--bf-text-medium);text-align:right}.bf-info-route,.bf-info-value-token{display:inline-flex;align-items:center;gap:4px}.bf-info-route .bf-token-icon,.bf-info-value-token .bf-token-icon{flex-shrink:0}.bf-info-route-arrow{display:inline-flex;align-items:center;color:var(--bf-text-medium);font-size:12px;line-height:1}.bf-root[data-view=history] .bf-info-route-arrow{color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.bf-info-value[data-tone=accent]{color:var(--ds-accent-primary-high, #f0640d)}.bf-info-value[data-tone=warning]{color:var(--ds-system-orange-high, #e56f00)}.bf-info-value[data-tone=danger]{color:var(--ds-system-red-default, #da0b2e)}.bf-root[data-view=history] .bf-info-label,.bf-root[data-view=history] .bf-liquidity-head{color:var(--ds-content-low, rgba(0, 0, 0, .4))}.bf-root[data-view=history] .bf-info-value{color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.bf-root[data-view=history] .bf-info-value[data-tone=accent]{color:var(--ds-accent-primary-high, #f0640d)}.bf-root[data-view=history] .bf-info-value[data-tone=warning]{color:var(--ds-system-orange-high, #e56f00)}.bf-root[data-view=history] .bf-info-value[data-tone=danger]{color:var(--ds-system-red-default, #da0b2e)}.bf-liquidity-status{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:999px;font-size:13px;font-weight:600;line-height:1.4;white-space:nowrap}.bf-liquidity-status:before{content:\\\"\\\";display:inline-block;width:7px;height:7px;border-radius:50%;flex-shrink:0}.bf-liquidity-status[data-status=normal],.bf-liquidity-status:not([data-status]){background:#00ceb21f;color:var(--ds-accent-primary-default, #f0640d)}.bf-liquidity-status[data-status=normal]:before,.bf-liquidity-status:not([data-status]):before{background:var(--ds-accent-primary-default, #f0640d)}.bf-liquidity-status[data-status=low]{background:#e56f001f;color:#e56f00}.bf-liquidity-status[data-status=low]:before{background:#e56f00}.bf-liquidity-status[data-status=rebalancing]{background:#da0b2e1a;color:#da0b2e}.bf-liquidity-status[data-status=rebalancing]:before{background:#da0b2e}.bf-liquidity-track{height:8px;overflow:hidden;border-radius:999px;background:var(--bf-surface-hover)}.bf-liquidity-track span{display:block;height:100%;border-radius:inherit;background:var(--ds-accent-primary-default, #f0640d)}.bf-error{padding:12px;border-radius:12px;background:#da0b2e1f;color:var(--ds-system-red-default, #da0b2e);font-size:13px;line-height:1.4}.bf-footer{display:flex;flex-shrink:0;flex-direction:column;align-items:center;gap:10px;padding:12px 24px 24px;background:var(--ds-bg-default, var(--bf-bg))}.bf-next{width:100%;min-height:48px;padding:12px;border:0;border-radius:12px;background:var(--ds-accent-primary-default, #f0640d);color:var(--ds-accent-primary-on, #000000);font:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;cursor:pointer}.bf-next:disabled{cursor:default;opacity:.6}.bf-powered{display:inline-flex;align-items:center;gap:3px;color:var(--bf-text-low);font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;text-decoration:none}.bf-powered:focus-visible{border-radius:4px;outline:2px solid var(--ds-accent-primary-default, #f0640d);outline-offset:2px}.bf-powered-brand{text-decoration:underline;text-underline-offset:2px}.bf-history-tabs{display:flex;align-items:center;gap:4px;width:100%}.bf-history-tab{position:relative;display:inline-flex;min-height:37px;align-items:center;justify-content:center;padding:8px 12px;border:0;background:transparent;color:var(--ds-content-lowest, rgba(0, 0, 0, .25));font:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:0;cursor:pointer}.bf-history-tab[data-active=true]{color:var(--ds-accent-primary-high, #f0640d)}.bf-history-tab[data-active=true]:after{position:absolute;right:8px;bottom:0;left:8px;height:3px;border-radius:999px;background:var(--ds-accent-primary-high, #f0640d);content:\\\"\\\"}.bf-history-tab-dot{position:absolute;top:8px;right:4px;width:6px;height:6px;border-radius:999px;background:var(--ds-system-red-default, #da0b2e)}.bf-history-list{display:flex;flex-direction:column;gap:12px}.bf-history-item{display:flex;flex-direction:column;gap:8px;padding:12px;border-radius:12px;background:var(--ds-surface-default, #ffffff);box-sizing:border-box}.bf-history-item-head,.bf-history-meta,.bf-history-route,.bf-history-route-token{display:inline-flex;align-items:center}.bf-history-item-head{justify-content:space-between;gap:12px;width:100%}.bf-history-status{display:inline-flex;min-height:24px;align-items:center;justify-content:center;padding:3px 8px;border-radius:4px;background:var(--ds-system-orange-low, #fcf5e9);color:var(--ds-system-orange-high, #e56f00);font-size:12px;font-weight:500;line-height:1.5;letter-spacing:0;white-space:nowrap}.bf-history-status[data-status=completed]{background:var(--ds-system-green-low, #e7f9f8);color:var(--ds-system-green-high, #00a0a2)}.bf-history-status[data-status=failed]{background:var(--ds-system-red-low, rgba(218, 11, 46, .12));color:var(--ds-system-red-default, #da0b2e)}.bf-history-time{min-width:0;color:var(--ds-content-low, rgba(0, 0, 0, .4));font-size:12px;font-weight:500;line-height:1.5;letter-spacing:0;text-align:right;white-space:nowrap}.bf-history-item-body{display:flex;flex-direction:column;gap:4px}.bf-history-amount{color:var(--ds-content-high, rgba(0, 0, 0, .85));font-size:16px;font-weight:600;line-height:1.5;letter-spacing:0}.bf-history-meta{justify-content:space-between;gap:12px;min-width:0}.bf-history-usd{min-width:0;color:var(--ds-content-low, rgba(0, 0, 0, .4));font-size:12px;font-weight:500;line-height:1.5;letter-spacing:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bf-history-route{justify-content:flex-end;gap:4px;min-width:0;color:var(--ds-content-high, rgba(0, 0, 0, .85));font-size:12px;font-weight:600;line-height:1.5;letter-spacing:0}.bf-history-route-token{gap:4px;min-width:0}.bf-history-route-token span:last-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bf-history-route-arrow{flex-shrink:0;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.bf-empty{padding:32px 16px;text-align:center;color:var(--ds-content-low, rgba(0, 0, 0, .4));font-size:14px}.bf-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media(max-width:767px){.bf-slippage-overlay{align-items:flex-end;padding:0}.bf-slippage-dialog{width:100%;height:auto;min-height:316px;max-height:82dvh;border-right:0;border-bottom:0;border-left:0;border-radius:24px 24px 0 0}}.wi-drawer-content .bf-header{padding:16px}.wi-drawer-content .bf-header .bf-header-btn:last-child{top:16px;right:16px}.wi-drawer-content .bf-body,.wi-drawer-content .bf-history-body{padding:16px}.bf-root[data-variant=embedded]{--bf-bg: #1c1c1d;--bf-surface: #212224;--bf-surface-hover: rgba(255, 255, 255, .08);--bf-border: rgba(255, 255, 255, .1);--bf-text-high: rgba(255, 255, 255, .85);--bf-text-medium: rgba(255, 255, 255, .65);--bf-text-low: rgba(255, 255, 255, .4);--bf-embedded-primary: #f3742e;--bf-embedded-on-primary: #ffffff;background:var(--bf-bg);color:#fff}.gou-shell[data-theme=light] .bf-root[data-variant=embedded]{--bf-bg: #ffffff;--bf-surface: #ffffff;--bf-surface-hover: rgba(0, 0, 0, .03);--bf-border: rgba(0, 0, 0, .06);--bf-text-high: rgba(0, 0, 0, .85);--bf-text-medium: rgba(0, 0, 0, .65);--bf-text-low: rgba(0, 0, 0, .4);color:#000}.bf-root[data-variant=embedded] .bf-header{min-height:64px;justify-content:flex-start;gap:12px;padding:16px 20px 12px;border-bottom-color:var(--bf-border);background:var(--bf-bg)}.bf-root[data-variant=embedded] .bf-header .bf-header-btn:last-child{top:16px;right:20px}.bf-root[data-variant=embedded] .bf-header-btn{color:var(--bf-text-medium)}.bf-root[data-variant=embedded] .bf-header-title{position:static;flex:1;padding-right:36px;transform:none;color:var(--bf-text-high);font-size:18px;font-weight:600;line-height:1.5;text-align:left}.bf-root[data-variant=embedded] .bf-header-spacer{display:none}.bf-root[data-variant=embedded] .bf-embedded-body{padding:20px}.bf-embedded-form{display:flex;width:100%;min-width:0;flex-direction:column;gap:20px;--bf-collapse-gap: 20px}.bf-embedded-fields{position:relative;display:flex;width:100%;flex-direction:column}.bf-embedded-field-group{display:flex;width:100%;flex-direction:column;gap:8px}.bf-embedded-amount-panel{display:flex;width:100%;min-width:0;flex-direction:column;padding:16px;border:1px solid rgba(255,255,255,.025);border-radius:12px;background:#212224;box-sizing:border-box}.gou-shell[data-theme=light] .bf-embedded-amount-panel{border-color:#00000014;background:#fff}.bf-embedded-amount-head{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.bf-embedded-amount-label{display:inline-flex;min-width:0;align-items:center;gap:8px;white-space:nowrap}.bf-embedded-amount-label strong{color:var(--bf-text-medium);font-size:14px;font-weight:700;line-height:1.5}.bf-embedded-amount-label>span{color:var(--bf-text-low);font-size:12px;font-weight:500;line-height:1.5}.bf-embedded-amount-label em{padding:2px 6px;border-radius:9999px;background:#f3742e1a;color:var(--bf-embedded-primary);font-size:11px;font-style:normal;font-weight:500;line-height:1.25}.bf-embedded-balance{display:inline-flex;min-width:0;max-width:100%;flex-shrink:1;align-items:center;justify-content:flex-end;gap:6px;overflow:hidden;color:var(--bf-text-medium);font-size:12px;font-weight:600;line-height:1.5;text-align:right;white-space:nowrap}.bf-embedded-balance>svg,.bf-embedded-balance>span:not(.bf-embedded-balance-value){flex-shrink:0}.bf-embedded-balance-value{min-width:0;overflow:hidden;text-overflow:ellipsis}.bf-embedded-balance button{flex-shrink:0;padding:2px 8px;border:0;border-radius:9999px;background:#f3742e26;color:var(--bf-embedded-primary);font:inherit;font-size:11px;font-weight:700;line-height:1.4;cursor:pointer}.bf-embedded-balance button:hover{background:#f3742e40}.bf-embedded-amount-main{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}.bf-embedded-token-pill{display:inline-flex;height:32px;flex-shrink:0;align-items:center;gap:6px;padding:0 12px 0 6px;border:1px solid rgba(255,255,255,.1);border-radius:9999px;background:#ffffff0d;color:#fff;font:inherit;font-size:14px;font-weight:700;line-height:1.5;cursor:pointer;perspective:220px;overflow:hidden;transition:transform .12s ease,background .15s ease}.gou-shell[data-theme=light] .bf-embedded-token-pill{border-color:#0000000f;background:#00000008;color:#000}.bf-embedded-token-pill:disabled{cursor:default;opacity:1}.bf-embedded-input-wrap{display:flex;min-width:0;flex:1}.bf-embedded-amount-input{width:100%;min-width:0;padding:0;border:0;outline:0;background:transparent;color:#fff;font:inherit;font-size:24px;font-size:clamp(18px,6cqi,24px);font-weight:700;line-height:32px;text-align:right}.gou-shell[data-theme=light] .bf-embedded-amount-input{color:#000}.bf-embedded-amount-input[data-size=medium]{font-size:20px;font-size:clamp(16px,5cqi,20px);line-height:28px}.bf-embedded-amount-input[data-size=small]{font-size:16px;font-size:clamp(14px,4cqi,16px);line-height:24px}.bf-embedded-amount-input::placeholder{color:var(--bf-text-low);opacity:1}.bf-embedded-field-error{color:#ff3c53;font-size:12px;font-weight:400;line-height:1.5}.bf-embedded-direction{position:relative;z-index:10;display:flex;width:100%;align-items:center;justify-content:center;margin:-12px 0}.bf-embedded-direction>span{display:inline-flex;width:36px;height:36px;align-items:center;justify-content:center;border-radius:9999px;background:var(--bf-embedded-primary);color:var(--bf-embedded-on-primary)}.bf-embedded-summary{display:flex;width:100%;flex-direction:column;gap:12px}.bf-embedded-details{display:flex;width:100%;flex-direction:column;gap:16px;padding:0 16px 16px;box-sizing:border-box}.bf-embedded-details .bf-info-section{gap:12px}.bf-embedded-details .bf-info-section+.bf-info-section{padding-top:0}.bf-embedded-details .bf-info-section h4{color:#fffc;font-size:14px;font-weight:700}.gou-shell[data-theme=light] .bf-embedded-details .bf-info-section h4{color:#000c}.bf-embedded-details .bf-info-rows{gap:12px}.bf-embedded-details .bf-info-row{min-height:20px;font-size:13px;font-weight:600}.bf-embedded-details .bf-info-label{gap:6px;color:var(--bf-text-medium);font-size:13px;font-weight:400}.bf-embedded-details .bf-info-value{color:var(--bf-text-medium);font-size:13px;font-weight:600}.bf-embedded-details .bf-info-value[data-tone=accent]{color:var(--bf-embedded-primary)}.bf-embedded-details .bf-info-route-arrow{color:var(--bf-text-medium)}.bf-embedded-liquidity{display:flex;flex-direction:column;gap:12px;padding:16px;border-radius:12px;background:var(--bf-surface)}.bf-embedded-liquidity>div{display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--bf-text-medium);font-size:14px}.bf-embedded-liquidity>div>span:last-child{text-transform:capitalize}.bf-embedded-liquidity-track{display:block;height:8px;overflow:hidden;border-radius:9999px;background:var(--bf-surface-hover)}.bf-embedded-liquidity-track>span{display:block;height:100%;border-radius:inherit;background:var(--bf-embedded-primary)}.bf-embedded-liquidity-error{padding:12px 16px;border:1px solid rgba(255,60,83,.3);border-radius:8px;background:#ff3c531a;color:#ff3c53;font-size:14px;line-height:1.5;text-align:center}.bf-embedded-action-footer{width:100%;flex-shrink:0}.bf-embedded-cta{display:flex;width:100%;height:52px;flex-shrink:0;align-items:center;justify-content:center;padding:0 16px;border:0;border-radius:12px;background:var(--bf-embedded-primary);color:var(--bf-embedded-on-primary);font:inherit;font-size:16px;font-weight:600;line-height:1.5;cursor:pointer;transition:opacity .15s ease,background-color .15s ease;box-sizing:border-box}.bf-embedded-cta:hover:not(:disabled){background:#f3742ee6}.bf-embedded-cta:disabled{cursor:not-allowed;opacity:.6}.bf-embedded-spinner{width:20px;height:20px;border:2px solid var(--bf-embedded-on-primary);border-top-color:transparent;border-radius:9999px;animation:bf-loading-spin .9s linear infinite}@media(max-width:767px){.bf-root[data-variant=embedded] .bf-header{min-height:50px;padding:12px 16px 8px;border-bottom:0}.bf-root[data-variant=embedded] .bf-header .bf-header-btn:last-child{display:none}.bf-root[data-variant=embedded] .bf-embedded-body{padding:12px 16px calc(env(safe-area-inset-bottom,0px) + 12px);overscroll-behavior:contain;scroll-padding-bottom:calc(env(safe-area-inset-bottom,0px) + 76px);-webkit-overflow-scrolling:touch}.bf-root[data-variant=embedded] .bf-embedded-action-footer{position:sticky;bottom:0;z-index:20;padding-top:12px;background:var(--bf-bg);isolation:isolate}.bf-root[data-variant=embedded] .bf-embedded-action-footer:after{position:absolute;z-index:-1;top:100%;right:0;left:0;height:calc(env(safe-area-inset-bottom,0px) + 32px);background:var(--bf-bg);box-shadow:0 16px 0 16px var(--bf-bg);content:\\\"\\\";pointer-events:none}.bf-root[data-variant=embedded][data-soft-keyboard-open=true] .bf-embedded-action-footer{position:static;padding-top:0}.bf-root[data-variant=embedded][data-soft-keyboard-open=true] .bf-embedded-action-footer:after{display:none}.bf-root[data-variant=embedded][data-soft-keyboard-open=true] .bf-embedded-body{scroll-padding-bottom:24px}.bf-embedded-amount-head{gap:8px}.bf-embedded-amount-label{gap:6px}.bf-embedded-balance{gap:4px}}\\n\")","\"use client\";\n\nimport * as React from \"react\";\nimport BigNumber from \"bignumber.js\";\nimport { parseNumeric } from \"../../../lib/numeric\";\nimport type { Environment } from \"../../../types\";\nimport type { TokenBalance, TokenInfo, TokenStats } from \"../types\";\nimport { useTokenHistory } from \"../queries/use-token-history\";\nimport { useTokenDetailVisibility } from \"../queries/use-token-detail-visibility\";\nimport { TokenPriceChart } from \"./TokenPriceChart\";\nimport { useExplorerUrlBuilder } from \"../../send/queries/use-chain-info\";\nimport { useChainDisplay } from \"../../../lib/remote-config\";\nimport {\n  formatWei,\n  formatTokenValue,\n  shortenAddress,\n} from \"../../../lib/format\";\nimport { cn } from \"../../../lib/utils\";\nimport { handleOutlinkClick, type OnOutlink } from \"../../../lib/outlink\";\nimport {\n  BackIcon,\n  CloseIcon,\n  CopyIcon,\n  CheckIcon,\n} from \"./icons\";\n\nexport interface TokenDetailViewProps {\n  env: Environment;\n  /** 모바일(전체화면)은 무한스크롤, PC(팝오버)는 \"Load more\" 버튼으로 페이지네이션. */\n  isMobile: boolean;\n  token: TokenBalance;\n  walletAddress: string;\n  stat?: TokenStats;\n  tokenInfo?: TokenInfo;\n  showBalance?: boolean;\n  onBack: () => void;\n  onClose: () => void;\n  onSend: (token: TokenBalance) => void;\n  onOutlink?: OnOutlink;\n}\n\nconst FALLBACK_TOKEN_ICON =\n  \"https://contents.crosstoken.io/wallet/token/images/FT.png\";\n\n/** 이력 항목의 자금 흐름 방향. bridge-*는 브릿지 컨트랙트가 상대일 때. */\ntype TokenHistoryDirection = \"receive\" | \"send\" | \"bridge-in\" | \"bridge-out\";\n\nconst HISTORY_DIRECTION_LABEL: Record<TokenHistoryDirection, string> = {\n  receive: \"Receive\",\n  send: \"Send\",\n  \"bridge-in\": \"Bridge In\",\n  \"bridge-out\": \"Bridge Out\",\n};\n\n/**\n * 이력 금액이 길어 한 줄을 넘기지 않도록 글자 수 기준으로 폰트 크기 티어를 고른다.\n * (부호 + 금액 + 심볼 전체 길이 기준. CSS에서 `data-size`별 font-size로 매핑.)\n */\nfunction historyAmountSize(text: string): \"md\" | \"sm\" | \"xs\" {\n  if (text.length > 24) return \"xs\";\n  if (text.length > 18) return \"sm\";\n  return \"md\";\n}\n\n/** unix timestamp(초 또는 밀리초) → \"3m\" / \"5h\" / \"2d\" 형태의 상대 시간. */\nfunction formatRelativeTime(timestamp: number): string {\n  if (!timestamp) return \"\";\n  const ms = timestamp < 1e12 ? timestamp * 1000 : timestamp;\n  const diffSec = Math.max(0, Math.floor((Date.now() - ms) / 1000));\n  if (diffSec < 60) return \"just now\";\n  const min = Math.floor(diffSec / 60);\n  if (min < 60) return `${min}m`;\n  const hr = Math.floor(min / 60);\n  if (hr < 24) return `about ${hr}h`;\n  const day = Math.floor(hr / 24);\n  if (day < 30) return `${day}d`;\n  const month = Math.floor(day / 30);\n  if (month < 12) return `${month}mo`;\n  return `${Math.floor(month / 12)}y`;\n}\n\n/**\n * 정수부에 천단위 콤마를 넣는다. `formatRawTokenAmount` 결과(콤마 없음) 보정용.\n * 정규식(백트래킹) 대신 선형 루프로 처리 — 코드베이스의 ReDoS 회피 관례를 따른다.\n */\nfunction groupInteger(value: string): string {\n  const [intPart, fracPart] = value.split(\".\");\n  let grouped = \"\";\n  for (let i = 0; i < intPart.length; i++) {\n    const fromEnd = intPart.length - i;\n    if (i > 0 && fromEnd % 3 === 0) grouped += \",\";\n    grouped += intPart[i];\n  }\n  return fracPart ? `${grouped}.${fracPart}` : grouped;\n}\n\n/**\n * raw(wei-scale) 토큰 금액을 소수점 2자리까지 **버림(floor)** 하여 표기한다.\n * 잔액은 음수가 없으므로 BigInt 나눗셈의 0 방향 절사 = floor. 전 과정을\n * BigInt로 유지하고(정밀도 손실 방지), 항상 2자리로 고정 표기한다.\n */\nfunction floorTokenAmount2(raw: string, decimals: number): string {\n  const value = BigInt(raw || \"0\");\n  if (decimals <= 2) {\n    const unit = 10n ** BigInt(decimals);\n    const whole = value / unit;\n    const cents = (value % unit) * 10n ** BigInt(2 - decimals);\n    return `${whole.toString()}.${cents.toString().padStart(2, \"0\")}`;\n  }\n  const hundredths = value / 10n ** BigInt(decimals - 2); // 2자리 초과분 버림\n  const whole = hundredths / 100n;\n  const cents = hundredths % 100n;\n  return `${whole.toString()}.${cents.toString().padStart(2, \"0\")}`;\n}\n\n/**\n * 텍스트가 컨테이너 폭을 넘으면 폰트 크기를 줄여 항상 한 줄에 맞춘다.\n * balance 처럼 길이가 가변적인 수치를 줄바꿈 없이 표기할 때 사용.\n *\n * 컨테이너 폭은 flex 레이아웃이 결정하므로(폰트 크기와 무관) 항상 최대\n * 크기에서 자연 폭을 측정한 뒤 축소 배율을 계산한다 — 진동/루프가 없다.\n */\nfunction useFitFontSize(text: string, maxPx: number, minPx: number) {\n  const containerRef = React.useRef<HTMLElement | null>(null);\n  const textRef = React.useRef<HTMLElement | null>(null);\n  const [fontSize, setFontSize] = React.useState(maxPx);\n\n  React.useLayoutEffect(() => {\n    const container = containerRef.current;\n    const el = textRef.current;\n    if (!container || !el) return;\n\n    const measure = () => {\n      const available = container.clientWidth;\n      if (!available) {\n        // 아직 레이아웃 전(폭 0). ResizeObserver가 폭이 잡히면 다시 부른다.\n        return;\n      }\n      // 자연 폭은 flex/max-width clamp 에 영향받지 않도록 body 에 붙인 분리형\n      // probe 로 잰다. el 을 직접 재면 stretch·max-width:100% 때문에 폭이 잘려\n      // scrollWidth 가 과소 보고된다.\n      const cs = window.getComputedStyle(el);\n      const probe = document.createElement(\"span\");\n      probe.textContent = el.textContent;\n      probe.style.cssText = [\n        \"position:absolute\",\n        \"left:-9999px\",\n        \"top:-9999px\",\n        \"visibility:hidden\",\n        \"white-space:nowrap\",\n        `font-family:${cs.fontFamily}`,\n        `font-weight:${cs.fontWeight}`,\n        `letter-spacing:${cs.letterSpacing}`,\n        `font-size:${maxPx}px`,\n      ].join(\";\");\n      document.body.appendChild(probe);\n      const natural = probe.offsetWidth;\n      document.body.removeChild(probe);\n\n      if (natural <= available) {\n        setFontSize(maxPx);\n        return;\n      }\n      const next = Math.max(minPx, Math.floor((maxPx * available) / natural));\n      setFontSize(next);\n    };\n\n    measure();\n\n    const ro = new ResizeObserver(measure);\n    ro.observe(container);\n    return () => ro.disconnect();\n  }, [text, maxPx, minPx]);\n\n  return { containerRef, textRef, fontSize };\n}\n\n/** circulating_supply(이미 토큰 단위 소수 문자열)를 콤마 + 최대 4자리 절사로 표기. */\nfunction formatSupply(value: string | undefined): string {\n  // `isNaN()` 검사로는 못 막는다 — 숫자가 아닌 문자열이면 생성자가 먼저 던진다.\n  const bn = parseNumeric(value);\n  if (!bn) return \"-\";\n  return bn.decimalPlaces(4, BigNumber.ROUND_DOWN).toFormat();\n}\n\ninterface SocialLink {\n  key: string;\n  label: string;\n  url: string;\n  icon: React.ReactNode;\n}\n\nfunction GlobeIcon() {\n  return (\n    <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden>\n      <circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n      <path\n        d=\"M3 12h18M12 3c2.5 2.5 3.8 5.8 3.8 9s-1.3 6.5-3.8 9c-2.5-2.5-3.8-5.8-3.8-9S9.5 5.5 12 3Z\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.6\"\n      />\n    </svg>\n  );\n}\n\nfunction TelegramIcon() {\n  return (\n    <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n      <path d=\"M21.9 4.3 18.6 20c-.2 1-.9 1.3-1.8.8l-4.9-3.6-2.4 2.3c-.3.3-.5.5-1 .5l.3-4.9 9-8.1c.4-.3-.1-.5-.6-.2L6.2 13.2l-4.8-1.5c-1-.3-1-1 .2-1.5L20.6 3c.9-.3 1.6.2 1.3 1.3Z\" />\n    </svg>\n  );\n}\n\nfunction XIcon() {\n  return (\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n      <path d=\"M18.9 2H22l-7.3 8.3L23.3 22h-6.8l-5.3-6.9L5.1 22H2l7.8-8.9L1 2h6.9l4.8 6.4L18.9 2Zm-1.2 18h1.9L7.2 4H5.2l12.5 16Z\" />\n    </svg>\n  );\n}\n\nfunction MediumIcon() {\n  return (\n    <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n      <path d=\"M6.9 6.5c0-.2-.1-.5-.3-.6L4.6 3.6V3.3h6.3l4.9 10.7 4.3-10.7H26v.3l-1.8 1.7c-.2.1-.2.3-.2.5v12.4c0 .2 0 .4.2.5l1.7 1.7v.3h-8.7v-.3l1.8-1.7c.2-.2.2-.2.2-.5V8.1l-5 12.6h-.7L7.9 8.1v8.5c0 .3.1.6.3.8l2.3 2.8v.3H4v-.3l2.3-2.8c.2-.2.3-.5.3-.8V6.5Z\" transform=\"scale(0.85)\" />\n    </svg>\n  );\n}\n\nexport function TokenDetailView({\n  env,\n  isMobile,\n  token,\n  walletAddress,\n  stat,\n  tokenInfo,\n  showBalance = true,\n  onBack,\n  onClose,\n  onSend,\n  onOutlink,\n}: TokenDetailViewProps) {\n  const decimals = token.quantity.decimals;\n  const [copied, setCopied] = React.useState(false);\n\n  const { buildTxUrl, chainInfo } = useExplorerUrlBuilder(\n    env,\n    token.chainId,\n    true,\n  );\n\n  // 섹션별 노출 여부(S3 wallet-info.json 원격 설정). 미로딩/실패 시 전부 노출.\n  const { visibility } = useTokenDetailVisibility(env);\n\n  const {\n    data,\n    isLoading: isHistoryLoading,\n    isError: isHistoryError,\n    fetchNextPage,\n    hasNextPage,\n    isFetchingNextPage,\n    refetch: refetchHistory,\n  } = useTokenHistory(env, token.chainId, token.address, walletAddress, true);\n\n  const historyItems = React.useMemo(\n    () => (data?.pages ? data.pages.flat() : []),\n    [data],\n  );\n\n  // 무한 스크롤(모바일 전용): 센티넬이 뷰포트에 들어오면 다음 페이지를 로드.\n  // PC(팝오버)에서는 관찰하지 않고 \"Load more\" 버튼으로 대체한다.\n  const loadMoreRef = React.useRef<HTMLDivElement>(null);\n  React.useEffect(() => {\n    if (!isMobile) return;\n    const el = loadMoreRef.current;\n    if (!el || !hasNextPage) return;\n    const observer = new IntersectionObserver(\n      (entries) => {\n        if (entries[0]?.isIntersecting && !isFetchingNextPage) {\n          fetchNextPage();\n        }\n      },\n      { rootMargin: \"120px\" },\n    );\n    observer.observe(el);\n    return () => observer.disconnect();\n  }, [isMobile, hasNextPage, isFetchingNextPage, fetchNextPage]);\n\n  const handleCopyAddress = React.useCallback(() => {\n    if (!navigator?.clipboard) return;\n    navigator.clipboard\n      .writeText(token.address)\n      .then(() => {\n        setCopied(true);\n        setTimeout(() => setCopied(false), 1500);\n      })\n      .catch(() => {});\n  }, [token.address]);\n\n  const balanceText = groupInteger(\n    floorTokenAmount2(token.quantity.numeric, decimals),\n  );\n  const fiatText = stat?.price\n    ? formatTokenValue(token.quantity.numeric, decimals, stat.price)\n    : null;\n\n  // 잔액이 길어 카드 폭을 넘으면 줄바꿈 대신 폰트 크기를 줄여 한 줄로 맞춘다.\n  const balanceDisplay = showBalance ? balanceText : \"••••\";\n  const {\n    containerRef: balanceLeftRef,\n    textRef: balanceAmountRef,\n    fontSize: balanceFontSize,\n  } = useFitFontSize(balanceDisplay, 30, 14);\n\n  // S3(chains.json) 표시명이 최우선 override, 없으면 백엔드 chainInfo.name,\n  // 그것도 없으면 chainId 폴백.\n  const chainDisplay = useChainDisplay(token.chainId, env);\n  const networkName =\n    chainDisplay.name ?? chainInfo?.name ?? `Chain ${token.chainId}`;\n  const explorerBase = chainInfo?.explorer_url;\n  const deployed = tokenInfo?.deployed ?? true;\n\n  const socialLinks = React.useMemo<SocialLink[]>(() => {\n    const links: SocialLink[] = [];\n    if (tokenInfo?.info_url)\n      links.push({ key: \"web\", label: \"Website\", url: tokenInfo.info_url, icon: <GlobeIcon /> });\n    if (tokenInfo?.telegram_url)\n      links.push({ key: \"tg\", label: \"Telegram\", url: tokenInfo.telegram_url, icon: <TelegramIcon /> });\n    if (tokenInfo?.x_url)\n      links.push({ key: \"x\", label: \"X\", url: tokenInfo.x_url, icon: <XIcon /> });\n    if (tokenInfo?.medium_url)\n      links.push({ key: \"medium\", label: \"Medium\", url: tokenInfo.medium_url, icon: <MediumIcon /> });\n    return links;\n  }, [tokenInfo]);\n\n  const outlink = (\n    e: React.MouseEvent<HTMLAnchorElement>,\n    url: string,\n    origin: \"token-network\" | \"token-social\" | \"token-tx\",\n    txHash?: string,\n  ) => {\n    handleOutlinkClick(\n      e,\n      url,\n      {\n        category: \"token-detail\",\n        origin,\n        payload: { address: token.address, txHash },\n      },\n      onOutlink,\n    );\n  };\n\n  return (\n    <>\n      <div className=\"wi-sticky-header\">\n        <div className=\"wi-header wi-header--receive\">\n          <button\n            type=\"button\"\n            className=\"wi-header-back\"\n            onClick={onBack}\n            aria-label=\"Back\"\n          >\n            <BackIcon size={24} />\n          </button>\n          <span className=\"wi-td-header-title\">\n            {/* icon_url 원본 이미지에 체인 배지가 이미 합성돼 있어 별도 오버레이는 두지 않는다. */}\n            <img\n              src={token.icon_url}\n              alt=\"\"\n              aria-hidden\n              className=\"wi-td-header-icon\"\n              width={24}\n              height={24}\n              draggable={false}\n              onError={(e) => {\n                e.currentTarget.src = FALLBACK_TOKEN_ICON;\n              }}\n            />\n            <span className=\"wi-td-header-name\">{token.symbol}</span>\n          </span>\n          <button\n            type=\"button\"\n            className=\"wi-header-action\"\n            onClick={onClose}\n            aria-label=\"Close\"\n          >\n            <CloseIcon size={24} />\n          </button>\n        </div>\n        <hr className=\"wi-divider\" />\n      </div>\n\n      <div className=\"wi-td-scroll\">\n        {/* Price chart (자체 SVG, 데이터 없으면 자동 숨김) */}\n        {visibility.chart && (\n          <TokenPriceChart\n            env={env}\n            chainId={token.chainId}\n            tokenAddress={token.address}\n          />\n        )}\n\n        {/* My Balance */}\n        {visibility.balance && (\n        <section className=\"wi-td-balance-card\">\n          <div\n            className=\"wi-td-balance-left\"\n            ref={balanceLeftRef as React.RefObject<HTMLDivElement>}\n          >\n            <span className=\"wi-td-balance-label\">My Balance</span>\n            <span\n              className=\"wi-td-balance-amount\"\n              ref={balanceAmountRef as React.RefObject<HTMLSpanElement>}\n              style={{ fontSize: balanceFontSize }}\n            >\n              {balanceDisplay}\n            </span>\n            {showBalance && fiatText && (\n              <span className=\"wi-td-balance-fiat\">{fiatText}</span>\n            )}\n          </div>\n          <button\n            type=\"button\"\n            className=\"wi-td-send-btn\"\n            onClick={() => onSend(token)}\n            aria-label={`Send ${token.symbol}`}\n            data-track=\"token_detail_send\"\n            data-track-id={token.symbol}\n          >\n            <span>Send</span>\n          </button>\n        </section>\n        )}\n\n        {/* Crypto Overview */}\n        {visibility.overview && (\n        <section className=\"wi-td-section\">\n          <h3 className=\"wi-td-section-title\">Crypto Overview</h3>\n          <div className=\"wi-td-rows\">\n            <div className=\"wi-td-row\">\n              <span className=\"wi-td-row-label\">Network</span>\n              {explorerBase ? (\n                <a\n                  className=\"wi-td-row-value wi-td-row-link\"\n                  href={explorerBase}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  onClick={(e) => outlink(e, explorerBase, \"token-network\")}\n                >\n                  <span>{networkName}</span>\n                  <ExternalLinkIcon />\n                </a>\n              ) : (\n                <span className=\"wi-td-row-value\">{networkName}</span>\n              )}\n            </div>\n\n            <div className=\"wi-td-row\">\n              <span className=\"wi-td-row-label\">Contract Address</span>\n              <button\n                type=\"button\"\n                className=\"wi-td-row-value wi-td-row-copy\"\n                onClick={handleCopyAddress}\n                aria-label=\"Copy contract address\"\n              >\n                <span>{shortenAddress(token.address)}</span>\n                {copied ? <CheckIcon size={14} /> : <CopyIcon size={14} />}\n              </button>\n            </div>\n\n            <div className=\"wi-td-row\">\n              <span className=\"wi-td-row-label\">Decimals</span>\n              <span className=\"wi-td-row-value\">{decimals}</span>\n            </div>\n\n            <div className=\"wi-td-row\">\n              <span className=\"wi-td-row-label\">Total Supply</span>\n              <span className=\"wi-td-row-value\">\n                {formatSupply(stat?.circulating_supply)}\n              </span>\n            </div>\n          </div>\n        </section>\n        )}\n\n        {/* Social Media */}\n        {visibility.social && socialLinks.length > 0 && (\n          <section className=\"wi-td-section\">\n            <h3 className=\"wi-td-section-title\">Social Media</h3>\n            <div className=\"wi-td-social-row\">\n              {socialLinks.map((link) => (\n                <a\n                  key={link.key}\n                  className=\"wi-td-social-btn\"\n                  href={link.url}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  aria-label={link.label}\n                  onClick={(e) => outlink(e, link.url, \"token-social\")}\n                >\n                  {link.icon}\n                </a>\n              ))}\n            </div>\n          </section>\n        )}\n\n        {/* Token History */}\n        {visibility.history && (\n        <section className=\"wi-td-section\">\n          <h3 className=\"wi-td-section-title\">Token History</h3>\n\n          {isHistoryLoading && (\n            <div className=\"wi-td-history-list\">\n              {Array.from({ length: 3 }, (_, i) => (\n                <div key={`td-sk-${i}`} className=\"wi-td-history-skeleton\">\n                  <div className=\"wi-skeleton-block\" style={{ width: 90, height: 14 }} />\n                  <div className=\"wi-skeleton-block\" style={{ width: 60, height: 14 }} />\n                </div>\n              ))}\n            </div>\n          )}\n\n          {!isHistoryLoading && isHistoryError && (\n            <div className=\"wi-td-history-empty\">\n              <span>Failed to load history.</span>\n              <button\n                type=\"button\"\n                className=\"wi-td-retry-btn\"\n                onClick={() => refetchHistory()}\n              >\n                Retry\n              </button>\n            </div>\n          )}\n\n          {!isHistoryLoading && !isHistoryError && historyItems.length === 0 && (\n            <div className=\"wi-td-history-empty\">\n              <span>No transactions found.</span>\n            </div>\n          )}\n\n          {!isHistoryLoading && !isHistoryError && historyItems.length > 0 && (\n            <div className=\"wi-td-history-list\">\n              {historyItems.map((item, idx) => {\n                // 브릿지 판별을 먼저 한다: 백엔드가 브릿지 컨트랙트에 alias를\n                // 붙여주면 to_alias/from_alias에 \"bridge\"가 담겨 온다. alias가\n                // 없으면 자연히 Send/Receive로 폴백된다.\n                const toIsBridge =\n                  item.to_alias?.toLowerCase().includes(\"bridge\") ?? false;\n                const fromIsBridge =\n                  item.from_alias?.toLowerCase().includes(\"bridge\") ?? false;\n                const direction: TokenHistoryDirection = toIsBridge\n                  ? \"bridge-out\"\n                  : fromIsBridge\n                    ? \"bridge-in\"\n                    : item.to?.toLowerCase() === walletAddress.toLowerCase()\n                      ? \"receive\"\n                      : \"send\";\n                const isInflow =\n                  direction === \"receive\" || direction === \"bridge-in\";\n                const counterparty = isInflow ? item.from : item.to;\n                const amount = groupInteger(\n                  floorTokenAmount2(item.amount, decimals),\n                );\n                const txUrl = buildTxUrl(item.tx);\n                const relative = formatRelativeTime(item.timestamp);\n\n                const rowInner = (\n                  <>\n                    <div className=\"wi-td-history-top\">\n                      <span\n                        className=\"wi-td-history-type\"\n                        data-direction={direction}\n                      >\n                        {HISTORY_DIRECTION_LABEL[direction]}\n                      </span>\n                      <span className=\"wi-td-history-counter\">\n                        {shortenAddress(counterparty ?? \"\")}\n                      </span>\n                    </div>\n                    <div className=\"wi-td-history-bottom\">\n                      <span className=\"wi-td-history-meta\">\n                        <span\n                          className=\"wi-td-history-status\"\n                          data-status={item.status}\n                        >\n                          {item.status === \"success\" ? \"Success\" : \"Failed\"}\n                        </span>\n                        {relative && (\n                          <span className=\"wi-td-history-time\">{relative}</span>\n                        )}\n                      </span>\n                      <span\n                        className=\"wi-td-history-amount\"\n                        data-size={historyAmountSize(\n                          `${isInflow ? \"+\" : \"-\"}${amount} ${token.symbol}`,\n                        )}\n                      >\n                        {isInflow ? \"+\" : \"-\"}\n                        {amount} {token.symbol}\n                      </span>\n                    </div>\n                  </>\n                );\n\n                const key = `${item.tx}-${item.log_index}-${idx}`;\n                return txUrl ? (\n                  <a\n                    key={key}\n                    className=\"wi-td-history-item wi-td-history-item--link\"\n                    href={txUrl}\n                    target=\"_blank\"\n                    rel=\"noopener noreferrer\"\n                    onClick={(e) => outlink(e, txUrl, \"token-tx\", item.tx)}\n                  >\n                    {rowInner}\n                  </a>\n                ) : (\n                  <div key={key} className=\"wi-td-history-item\">\n                    {rowInner}\n                  </div>\n                );\n              })}\n\n              {hasNextPage &&\n                (isMobile ? (\n                  <div ref={loadMoreRef} className=\"wi-td-history-sentinel\">\n                    {isFetchingNextPage && (\n                      <span className=\"wi-td-history-loading\">Loading…</span>\n                    )}\n                  </div>\n                ) : (\n                  <button\n                    type=\"button\"\n                    className=\"wi-td-loadmore-btn\"\n                    onClick={() => fetchNextPage()}\n                    disabled={isFetchingNextPage}\n                  >\n                    {isFetchingNextPage ? \"Loading…\" : \"Load more\"}\n                  </button>\n                ))}\n            </div>\n          )}\n        </section>\n        )}\n      </div>\n    </>\n  );\n}\n\nfunction ExternalLinkIcon() {\n  return (\n    <svg\n      width=\"14\"\n      height=\"14\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      className={cn(\"wi-td-external-icon\")}\n      aria-hidden\n    >\n      <path\n        d=\"M14 4h6v6M20 4l-9 9M18 13v5a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.7\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n","import { useInfiniteQuery } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type { TokenHistoryItem, TokenHistoryResponse } from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const TOKEN_HISTORY_QUERY_KEY = \"token-history\";\n\n/** 커서 페이지네이션 키. 직전 페이지 마지막 항목에서 파생된다. */\ninterface HistoryCursor {\n  last_timestamp: number;\n  last_log_index: number;\n  last_tx_index: number;\n}\n\n/**\n * 특정 토큰의 거래 이력을 `/v1/public/token/historical`에서 무한스크롤로 조회.\n * dapp-ui가 이미 사용하는 walletServer 호스트를 그대로 쓴다.\n *\n * 페이지네이션: 응답이 빈 배열이면 다음 페이지 없음으로 판단한다. 마지막\n * 부분 페이지(항목 존재) 뒤에 빈 페이지 요청이 한 번 더 발생할 수 있으나,\n * 그 시점에 `hasNextPage`가 false로 확정된다.\n */\nexport function useTokenHistory(\n  env: Environment,\n  chainId: number,\n  token: string,\n  wallet: string,\n  enabled = true,\n) {\n  return useInfiniteQuery<TokenHistoryItem[], Error>({\n    queryKey: [TOKEN_HISTORY_QUERY_KEY, env, chainId, token.toLowerCase(), wallet.toLowerCase()],\n    initialPageParam: undefined,\n    queryFn: async ({ pageParam, signal }) => {\n      const baseUrl = await getApiBase(\"walletServer\", env);\n      const params = new URLSearchParams({\n        chain_id: String(chainId),\n        token,\n        wallet,\n      });\n      const cursor = pageParam as HistoryCursor | undefined;\n      if (cursor) {\n        params.set(\"last_timestamp\", String(cursor.last_timestamp));\n        params.set(\"last_log_index\", String(cursor.last_log_index));\n        params.set(\"last_tx_index\", String(cursor.last_tx_index));\n      }\n      const res = await fetch(\n        `${baseUrl}/v1/public/token/historical?${params}`,\n        { cache: \"no-store\", signal },\n      );\n      if (!res.ok) {\n        throw new Error(`Failed to fetch token history: ${res.status}`);\n      }\n      const json: TokenHistoryResponse = await res.json();\n      if (json.code !== 200) {\n        throw new Error(`Token history API error: ${json.code}`);\n      }\n      return json.data ?? [];\n    },\n    getNextPageParam: (lastPage): HistoryCursor | undefined => {\n      if (!lastPage || lastPage.length === 0) return undefined;\n      const last = lastPage[lastPage.length - 1];\n      return {\n        last_timestamp: last.timestamp,\n        last_log_index: last.log_index,\n        last_tx_index: last.tx_index,\n      };\n    },\n    enabled: enabled && !!wallet && !!token && chainId > 0,\n    staleTime: 10_000,\n  });\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport type { Environment } from \"../../../types\";\nimport { fetchRemoteConfig, type AppsConfig } from \"../../../lib/remote-config\";\n\n/**\n * 토큰 상세화면 각 섹션의 노출 여부를 S3(CDN) 원격 설정으로 제어.\n *\n * 설정은 기존 `apps.json`의 `tokenDetail.sections`에 둔다. 값은 섹션별 boolean.\n * env별 차이는 apps.json 파일 자체가 환경마다 분리돼 있어(stg-contents vs\n * contents) 파일별로 값을 다르게 주면 되므로, 값은 단순 boolean으로만 받는다.\n *\n * ```json\n * {\n *   \"tokenDetail\": {\n *     \"sections\": {\n *       \"chart\":    true,\n *       \"overview\": true,\n *       \"social\":   true\n *     }\n *   }\n * }\n * ```\n *\n * 기본값(설정 로딩 전 / fetch 실패 / 키 누락): **history·balance만 노출**,\n * chart·overview·social 은 숨김 → S3에서 명시적으로 켜야 노출된다(opt-in).\n * `balance` 섹션은 My Balance + Send 버튼을 함께 포함하므로 하나로 토글된다.\n */\nexport type TokenDetailSectionKey =\n  | \"chart\"\n  | \"balance\"\n  | \"overview\"\n  | \"social\"\n  | \"history\";\n\nexport type TokenDetailSectionVisibility = Record<\n  TokenDetailSectionKey,\n  boolean\n>;\n\n/** 기본값: history·balance만 노출, 나머지는 S3에서 opt-in. */\nconst DEFAULT_VISIBILITY: TokenDetailSectionVisibility = {\n  chart: false,\n  balance: true,\n  overview: false,\n  social: false,\n  history: true,\n};\n\nconst SECTION_KEYS = Object.keys(DEFAULT_VISIBILITY) as TokenDetailSectionKey[];\n\nconst APPS_CONFIG_FILE = \"apps.json\";\n\nexport interface TokenDetailVisibilityResult {\n  visibility: TokenDetailSectionVisibility;\n  /** 원격 설정 fetch가 끝났는지. false면 아직 기본값을 쓰는 중. */\n  isLoaded: boolean;\n}\n\nexport function useTokenDetailVisibility(\n  env: Environment,\n): TokenDetailVisibilityResult {\n  const [state, setState] = React.useState<TokenDetailVisibilityResult>({\n    visibility: DEFAULT_VISIBILITY,\n    isLoaded: false,\n  });\n\n  React.useEffect(() => {\n    let alive = true;\n    fetchRemoteConfig<AppsConfig>(APPS_CONFIG_FILE, env).then((config) => {\n      if (!alive) return;\n      const raw = config?.tokenDetail?.sections;\n      const visibility: TokenDetailSectionVisibility = { ...DEFAULT_VISIBILITY };\n      if (raw) {\n        for (const key of SECTION_KEYS) {\n          const value = raw[key];\n          if (typeof value === \"boolean\") visibility[key] = value;\n        }\n      }\n      setState({ visibility, isLoaded: true });\n    });\n    return () => {\n      alive = false;\n    };\n  }, [env]);\n\n  return state;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport type { Environment } from \"../../../types\";\nimport type { TokenPriceInterval, TokenPricePoint } from \"../types\";\nimport { useTokenPriceHistory } from \"../queries/use-token-price-history\";\nimport { formatPrice } from \"../../../lib/format\";\n\nexport interface TokenPriceChartProps {\n  env: Environment;\n  chainId: number;\n  tokenAddress: string;\n}\n\nconst UP_COLOR = \"var(--ds-system-green-high, #00a869)\";\nconst DOWN_COLOR = \"var(--wi-error, #e70077)\";\n\nconst CHART_HEIGHT = 150;\nconst PAD_X = 3;\nconst PAD_TOP = 18;\nconst PAD_BOTTOM = 18;\n\n/** raw interval → 표시 라벨. 순서가 탭 순서를 결정. */\nconst INTERVALS: ReadonlyArray<{ key: TokenPriceInterval; label: string }> = [\n  { key: \"10m\", label: \"10m\" },\n  { key: \"1h\", label: \"1H\" },\n  { key: \"1d\", label: \"1D\" },\n  { key: \"7d\", label: \"1W\" },\n  { key: \"30d\", label: \"1M\" },\n  { key: \"1y\", label: \"1Y\" },\n];\n\nconst LONG_RANGE: ReadonlySet<TokenPriceInterval> = new Set([\n  \"7d\",\n  \"30d\",\n  \"1y\",\n]);\n\nfunction useElementWidth() {\n  const ref = React.useRef<HTMLDivElement>(null);\n  const [width, setWidth] = React.useState(0);\n  React.useEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    const ro = new ResizeObserver((entries) => {\n      setWidth(entries[0]?.contentRect.width ?? 0);\n    });\n    ro.observe(el);\n    setWidth(el.clientWidth);\n    return () => ro.disconnect();\n  }, []);\n  return [ref, width] as const;\n}\n\n/** unix초 → 로컬 시간 라벨. 장기 구간은 날짜까지 표기. */\nfunction formatPointTime(unixSec: number, longRange: boolean): string {\n  const d = new Date(unixSec * 1000);\n  const opts: Intl.DateTimeFormatOptions = longRange\n    ? { month: \"short\", day: \"numeric\", hour: \"numeric\", minute: \"2-digit\" }\n    : { hour: \"numeric\", minute: \"2-digit\" };\n  return new Intl.DateTimeFormat(\"en-US\", opts).format(d);\n}\n\ninterface Geometry {\n  values: number[];\n  points: Array<{ x: number; y: number }>;\n  linePath: string;\n  areaPath: string;\n  maxIdx: number;\n  minIdx: number;\n}\n\nfunction buildGeometry(values: number[], width: number): Geometry | null {\n  if (width <= 0 || values.length === 0) return null;\n\n  // 0/1 포인트는 평평한 2점으로 패딩한다.\n  const vals = values.length === 1 ? [values[0], values[0]] : values;\n  const n = vals.length;\n\n  let max = vals[0];\n  let min = vals[0];\n  let maxIdx = 0;\n  let minIdx = 0;\n  for (let i = 1; i < n; i++) {\n    if (vals[i] > max) {\n      max = vals[i];\n      maxIdx = i;\n    }\n    if (vals[i] < min) {\n      min = vals[i];\n      minIdx = i;\n    }\n  }\n  const range = max - min || 1;\n  const innerW = Math.max(1, width - PAD_X * 2);\n  const innerH = CHART_HEIGHT - PAD_TOP - PAD_BOTTOM;\n\n  const xFor = (i: number) =>\n    n === 1 ? width / 2 : PAD_X + (i / (n - 1)) * innerW;\n  const yFor = (v: number) => PAD_TOP + (1 - (v - min) / range) * innerH;\n\n  const points = vals.map((v, i) => ({ x: xFor(i), y: yFor(v) }));\n\n  const linePath = points\n    .map((p, i) => `${i === 0 ? \"M\" : \"L\"}${p.x.toFixed(2)},${p.y.toFixed(2)}`)\n    .join(\" \");\n\n  const first = points[0];\n  const last = points[points.length - 1];\n  const areaPath = `${linePath} L${last.x.toFixed(2)},${CHART_HEIGHT} L${first.x.toFixed(2)},${CHART_HEIGHT} Z`;\n\n  return { values: vals, points, linePath, areaPath, maxIdx, minIdx };\n}\n\nexport function TokenPriceChart({\n  env,\n  chainId,\n  tokenAddress,\n}: TokenPriceChartProps) {\n  const [interval, setInterval] = React.useState<TokenPriceInterval>(\"1d\");\n  const [hoverIdx, setHoverIdx] = React.useState<number | null>(null);\n  const gradientId = React.useId();\n  const [plotRef, width] = useElementWidth();\n\n  const { data, isLoading, isError } = useTokenPriceHistory(\n    env,\n    chainId,\n    tokenAddress,\n    interval,\n    true,\n  );\n\n  // 시간순 정렬 + 유효 숫자만.\n  const series = React.useMemo(() => {\n    const rows: TokenPricePoint[] = (data ?? [])\n      .filter((p) => Number.isFinite(Number(p.price)))\n      .slice()\n      .sort((a, b) => a.updated_at - b.updated_at);\n    return rows;\n  }, [data]);\n\n  const values = React.useMemo(() => series.map((p) => Number(p.price)), [series]);\n  const geometry = React.useMemo(\n    () => buildGeometry(values, width),\n    [values, width],\n  );\n\n  // interval 이 바뀌면 hover 해제.\n  React.useEffect(() => {\n    setHoverIdx(null);\n  }, [interval]);\n\n  const hasData = series.length > 0;\n  const currentIdx =\n    hoverIdx !== null && hoverIdx < values.length\n      ? hoverIdx\n      : values.length - 1;\n  const currentValue = hasData ? values[currentIdx] : 0;\n  const firstValue = hasData ? values[0] : 0;\n  const changePct =\n    firstValue !== 0 ? ((currentValue - firstValue) / firstValue) * 100 : 0;\n  const isUp = changePct >= 0;\n  const color = isUp ? UP_COLOR : DOWN_COLOR;\n\n  const changeText =\n    (isUp ? \"+\" : \"-\") +\n    (Math.floor(Math.abs(changePct) * 100) / 100).toFixed(2) +\n    \"%\";\n\n  const whenText =\n    hoverIdx !== null && series[hoverIdx]\n      ? formatPointTime(series[hoverIdx].updated_at, LONG_RANGE.has(interval))\n      : \"\";\n\n  const handlePointerMove = (e: React.PointerEvent<SVGRectElement>) => {\n    if (!geometry || width <= 0) return;\n    const rect = e.currentTarget.getBoundingClientRect();\n    const relX = e.clientX - rect.left;\n    const n = geometry.values.length;\n    if (n <= 1) {\n      setHoverIdx(0);\n      return;\n    }\n    const innerW = Math.max(1, width - PAD_X * 2);\n    const ratio = Math.min(1, Math.max(0, (relX - PAD_X) / innerW));\n    // series 길이 기준으로 인덱스를 매핑(패딩된 geometry가 아니라 실제 series).\n    const idx = Math.round(ratio * (series.length - 1));\n    setHoverIdx(Math.min(series.length - 1, Math.max(0, idx)));\n  };\n\n  const clearHover = () => setHoverIdx(null);\n\n  // 데이터 없음/에러 시 차트 섹션 자체를 접는다(레이아웃 점프 최소화 위해 로딩만 스켈레톤).\n  if (!isLoading && (isError || !hasData)) return null;\n\n  const hoverPoint =\n    hoverIdx !== null && geometry && geometry.points[hoverIdx]\n      ? geometry.points[hoverIdx]\n      : null;\n\n  const maxPoint = geometry?.points[geometry.maxIdx];\n  const minPoint = geometry?.points[geometry.minIdx];\n  const clampLabelX = (x: number) =>\n    Math.min(Math.max(x, 24), Math.max(24, width - 24));\n\n  return (\n    <section className=\"wi-td-chart\">\n      <div className=\"wi-td-chart-panel\">\n        <span className=\"wi-td-chart-price\">\n          {hasData ? formatPrice(currentValue) : \"—\"}\n        </span>\n        {hasData && (\n          <span className=\"wi-td-chart-change\" data-positive={isUp}>\n            <span>{changeText}</span>\n            {whenText && <span className=\"wi-td-chart-when\">{whenText}</span>}\n          </span>\n        )}\n      </div>\n\n      <div ref={plotRef} className=\"wi-td-chart-plot\">\n        {isLoading && !hasData ? (\n          <div className=\"wi-skeleton-block wi-td-chart-skeleton\" />\n        ) : (\n          geometry &&\n          width > 0 && (\n            <svg\n              className=\"wi-td-chart-svg\"\n              width={width}\n              height={CHART_HEIGHT}\n              role=\"img\"\n              aria-label=\"Price chart\"\n            >\n              <defs>\n                <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                  <stop offset=\"0%\" stopColor={color} stopOpacity={0.28} />\n                  <stop offset=\"100%\" stopColor={color} stopOpacity={0} />\n                </linearGradient>\n              </defs>\n\n              <path d={geometry.areaPath} fill={`url(#${gradientId})`} />\n              <path\n                d={geometry.linePath}\n                fill=\"none\"\n                stroke={color}\n                strokeWidth={2}\n                strokeLinejoin=\"round\"\n                strokeLinecap=\"round\"\n              />\n\n              {maxPoint && (\n                <text\n                  className=\"wi-td-chart-minmax\"\n                  x={clampLabelX(maxPoint.x)}\n                  y={Math.max(11, maxPoint.y - 7)}\n                  textAnchor=\"middle\"\n                >\n                  {formatPrice(geometry.values[geometry.maxIdx])}\n                </text>\n              )}\n              {minPoint && geometry.minIdx !== geometry.maxIdx && (\n                <text\n                  className=\"wi-td-chart-minmax\"\n                  x={clampLabelX(minPoint.x)}\n                  y={Math.min(CHART_HEIGHT - 5, minPoint.y + 14)}\n                  textAnchor=\"middle\"\n                >\n                  {formatPrice(geometry.values[geometry.minIdx])}\n                </text>\n              )}\n\n              {hoverPoint && (\n                <>\n                  <line\n                    className=\"wi-td-chart-cursor\"\n                    x1={hoverPoint.x}\n                    y1={PAD_TOP - 6}\n                    x2={hoverPoint.x}\n                    y2={CHART_HEIGHT}\n                  />\n                  <circle\n                    cx={hoverPoint.x}\n                    cy={hoverPoint.y}\n                    r={4}\n                    fill={color}\n                    stroke=\"var(--wi-surface-bg, #121212)\"\n                    strokeWidth={2}\n                  />\n                </>\n              )}\n\n              <rect\n                x={0}\n                y={0}\n                width={width}\n                height={CHART_HEIGHT}\n                fill=\"transparent\"\n                style={{ touchAction: \"none\" }}\n                onPointerMove={handlePointerMove}\n                onPointerDown={handlePointerMove}\n                onPointerLeave={clearHover}\n                onPointerCancel={clearHover}\n              />\n            </svg>\n          )\n        )}\n      </div>\n\n      <div className=\"wi-td-chart-tabs\" role=\"tablist\">\n        {INTERVALS.map((tab) => (\n          <button\n            key={tab.key}\n            type=\"button\"\n            role=\"tab\"\n            aria-selected={interval === tab.key}\n            className=\"wi-td-chart-tab\"\n            data-active={interval === tab.key || undefined}\n            onClick={() => setInterval(tab.key)}\n            data-track=\"token_detail_interval\"\n            data-track-id={tab.key}\n          >\n            {tab.label}\n          </button>\n        ))}\n      </div>\n    </section>\n  );\n}\n","import { useQuery, keepPreviousData } from \"@tanstack/react-query\";\nimport type { Environment } from \"../../../types\";\nimport type {\n  TokenPriceHistoryResponse,\n  TokenPriceInterval,\n  TokenPricePoint,\n} from \"../types\";\nimport { getApiBase } from \"../../../lib/remote-config\";\n\nexport const TOKEN_PRICE_HISTORY_QUERY_KEY = \"token-price-history\";\n\n/**\n * 특정 토큰의 가격 히스토리를 `/v1/public/token/price/historical`에서 조회.\n * dapp-ui가 이미 사용하는 walletServer 호스트를 그대로 쓴다. interval 전환 시\n * `keepPreviousData`로 이전 데이터를 유지해 차트 깜빡임을 막는다.\n */\nexport function useTokenPriceHistory(\n  env: Environment,\n  chainId: number,\n  token: string,\n  interval: TokenPriceInterval,\n  enabled = true,\n) {\n  return useQuery<TokenPricePoint[]>({\n    queryKey: [\n      TOKEN_PRICE_HISTORY_QUERY_KEY,\n      env,\n      chainId,\n      token.toLowerCase(),\n      interval,\n    ],\n    queryFn: async ({ signal }) => {\n      const baseUrl = await getApiBase(\"walletServer\", env);\n      const params = new URLSearchParams({\n        chain_id: String(chainId),\n        token,\n        interval,\n      });\n      const res = await fetch(\n        `${baseUrl}/v1/public/token/price/historical?${params}`,\n        { cache: \"no-store\", signal },\n      );\n      if (!res.ok) {\n        throw new Error(`Failed to fetch token price history: ${res.status}`);\n      }\n      const json: TokenPriceHistoryResponse = await res.json();\n      if (json.code !== 200) {\n        throw new Error(`Token price history API error: ${json.code}`);\n      }\n      return json.data ?? [];\n    },\n    enabled: enabled && !!token && chainId > 0,\n    staleTime: 30_000,\n    placeholderData: keepPreviousData,\n  });\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../../lib/utils\";\nimport type { Environment } from \"../../../types\";\nimport { BackIcon, CloseIcon } from \"../../wallet-info/components/icons\";\nimport {\n  onePopLandingUrl,\n  oneUsdBridgeUrl,\n  type OnePopLandingPage,\n} from \"../landing\";\nimport type {\n  OnePopActivityItem,\n  OnePopActivityStatus,\n  OnePopSummary,\n} from \"../types\";\nimport {\n  OnePopActivityIcon,\n  OnePopChevronRightIcon,\n  OnePopClaimIcon,\n  OnePopHandleIcon,\n  OnePopHeroArt,\n  OnePopInstantIcon,\n  OnePopLogo,\n  OnePopRefundIcon,\n  OnePopSendIcon,\n  XBadgeIcon,\n} from \"./icons\";\nimport \"../styles/onepop.css\";\n\n/** 리스트 미리보기에 노출하는 최대 활동 수. 나머지는 \"See all activity\". */\nconst RECENT_ACTIVITY_LIMIT = 6;\n\nconst STATUS_LABEL: Record<OnePopActivityStatus, string> = {\n  claimed: \"Claimed\",\n  waiting: \"Waiting\",\n  refunded: \"Refunded\",\n  ready: \"Ready\",\n};\n\n/**\n * raw(wei-scale) 금액을 소수점 2자리 **버림**으로 표기한다. 전 과정을 BigInt로\n * 유지해 2^53 이상에서도 정밀도가 깨지지 않는다 (CLAUDE.md §4). 잔액은 음수가\n * 없으므로 BigInt 나눗셈의 0 방향 절사 = floor.\n */\nfunction formatAmount2(raw: string, decimals: number): string {\n  let value: bigint;\n  try {\n    value = BigInt(raw || \"0\");\n  } catch {\n    return \"0.00\";\n  }\n  const negative = value < 0n;\n  if (negative) value = -value;\n\n  let whole: bigint;\n  let cents: bigint;\n  if (decimals <= 2) {\n    const unit = 10n ** BigInt(decimals);\n    whole = value / unit;\n    cents = (value % unit) * 10n ** BigInt(2 - decimals);\n  } else {\n    const hundredths = value / 10n ** BigInt(decimals - 2); // 2자리 초과분 버림\n    whole = hundredths / 100n;\n    cents = hundredths % 100n;\n  }\n  return `${negative ? \"-\" : \"\"}${groupInteger(whole.toString())}.${cents\n    .toString()\n    .padStart(2, \"0\")}`;\n}\n\n/**\n * 정수부 천단위 콤마. 코드베이스 관례를 따라 정규식(백트래킹) 대신 선형 루프.\n */\nfunction groupInteger(intPart: string): string {\n  let grouped = \"\";\n  for (let i = 0; i < intPart.length; i++) {\n    const fromEnd = intPart.length - i;\n    if (i > 0 && fromEnd % 3 === 0) grouped += \",\";\n    grouped += intPart[i];\n  }\n  return grouped;\n}\n\n/** unix timestamp(초 또는 밀리초) → \"2h ago\" 형태의 상대 시간. */\nfunction formatRelativeTime(timestamp: number): string {\n  if (!timestamp) return \"\";\n  const ms = timestamp < 1e12 ? timestamp * 1000 : timestamp;\n  const diffSec = Math.max(0, Math.floor((Date.now() - ms) / 1000));\n  if (diffSec < 60) return \"just now\";\n  const min = Math.floor(diffSec / 60);\n  if (min < 60) return `${min}m ago`;\n  const hr = Math.floor(min / 60);\n  if (hr < 24) return `${hr}h ago`;\n  const day = Math.floor(hr / 24);\n  if (day < 30) return `${day}d ago`;\n  const month = Math.floor(day / 30);\n  if (month < 12) return `${month}mo ago`;\n  return `${Math.floor(month / 12)}y ago`;\n}\n\n/** `@handle` 표기 — 호출측이 `@`를 붙여 보내도 중복되지 않게 한다. */\nfunction withAt(handle: string): string {\n  const trimmed = handle.trim();\n  if (!trimmed) return \"\";\n  return trimmed.startsWith(\"@\") ? trimmed : `@${trimmed}`;\n}\n\nexport interface OnePopBodyProps {\n  /** ONEpop 서비스 웹 딥링크의 환경. 미지정 시 production. */\n  env?: Environment;\n  /** Available 카드에 표시할 ONEUSD 잔액 (표시용 포맷 완료 문자열). */\n  balanceDisplay?: string;\n  /** Available 카드의 토큰 아이콘. 미지정 시 아이콘 자리를 비운다. */\n  balanceIconUrl?: string;\n  /** 잔액 조회 중이면 스켈레톤. */\n  isBalanceLoading?: boolean;\n  /** 호스트가 `@nexus-cross/pop`으로 조회해 주입하는 상태. 미주입 = 빈 상태. */\n  summary?: OnePopSummary;\n  /** 브랜드 워드마크 이미지. 미지정 시 인라인 SVG 재현본. */\n  logoSrc?: string;\n  /** 히어로 일러스트 이미지. 미지정 시 인라인 SVG 재현본. */\n  heroSrc?: string;\n  onBack: () => void;\n  onClose: () => void;\n  /**\n   * Send POP 카드 + \"Send your first POP!\" CTA override. 미주입 시 ONEpop 웹\n   * 센드 페이지(`/pop`)를 새 탭으로 연다. 다른 액션도 같은 규칙 — 전부 서비스\n   * 페이지 이동이라 disabled 상태가 없다.\n   */\n  onSend?: () => void;\n  /** Claim POPs 카드 + 클레임 배너 override. 기본 `/pop/claim`. */\n  onClaim?: () => void;\n  /**\n   * Activity 카드 + \"See all activity\" override. 기본 `/pop/activity`.\n   * Activity 카드는 `summary.xConnected` 가 true 일 때만 렌더된다.\n   */\n  onActivity?: () => void;\n  /**\n   * 활동 화면으로 나가는 모든 경로(Activity 카드 / `See all activity` /\n   * `N replies waiting` 배너)에서 **추가로** 호출되는 알림 훅. `onActivity` 와\n   * 달리 기본 동작을 대체하지 않는다 — 답장을 확인 처리해\n   * `repliesWaitingCount` 를 0 으로 떨어뜨리는 용도다.\n   */\n  onRepliesSeen?: () => void;\n  /** \"You might have POPs waiting\" 배너 override. 기본 `/pop/board`. */\n  onBoard?: () => void;\n  /** \"Get ONEUSD ›\" override. 기본 게임토큰 브리지(`/gametoken/bridge`). */\n  onGetToken?: () => void;\n  /**\n   * \"Get ONEUSD ›\" 자리를 통째로 대체하는 노드. 지갑을 떠나지 않고 Get ONEUSD\n   * 위젯(Swap / Bridge / Transfer 선택 모달)을 그 자리에서 열기 위한 슬롯이다 —\n   * 위젯은 wagmi 어댑터가 필요해 dapp-ui 가 직접 마운트할 수 없으므로,\n   * `@nexus-cross/connect-kit-react` 가 `<CrossGetOneUsd mode=\"button\"\n   * buttonComponent={OnePopGetTokenTrigger}>` 를 주입한다.\n   *\n   * 주입되면 `onGetToken`/기본 딥링크는 쓰이지 않는다.\n   */\n  getTokenSlot?: React.ReactNode;\n}\n\nexport function OnePopBody({\n  env = \"production\",\n  balanceDisplay,\n  balanceIconUrl,\n  isBalanceLoading,\n  summary,\n  logoSrc,\n  heroSrc,\n  onBack,\n  onClose,\n  onSend,\n  onClaim,\n  onActivity,\n  onRepliesSeen,\n  onBoard,\n  onGetToken,\n  getTokenSlot,\n}: OnePopBodyProps) {\n  const activity = summary?.activity ?? [];\n  const hasActivity = activity.length > 0;\n  const claimableCount = summary?.claimableCount ?? 0;\n  const repliesWaitingCount = summary?.repliesWaitingCount ?? 0;\n  const isLoading = summary?.isLoading ?? false;\n  const xConnected = summary?.xConnected ?? false;\n\n  // 모든 액션의 기본 동작은 ONEpop 서비스 페이지 딥링크다. override 콜백이\n  // 있으면 그것만 실행한다.\n  const openLanding = React.useCallback(\n    (page: OnePopLandingPage) => {\n      window.open(onePopLandingUrl(env, page), \"_blank\", \"noopener,noreferrer\");\n    },\n    [env],\n  );\n  const handleSend = React.useCallback(\n    () => (onSend ? onSend() : openLanding(\"send\")),\n    [onSend, openLanding],\n  );\n  const handleClaim = React.useCallback(\n    () => (onClaim ? onClaim() : openLanding(\"claim\")),\n    [onClaim, openLanding],\n  );\n  const handleActivity = React.useCallback(() => {\n    // 활동 화면에 답장이 보이므로, 나가는 시점에 확인 처리한다. 목적지 이동과\n    // 독립이라 override 여부와 무관하게 항상 알린다.\n    onRepliesSeen?.();\n    if (onActivity) return onActivity();\n    openLanding(\"activity\");\n  }, [onActivity, onRepliesSeen, openLanding]);\n  const handleBoard = React.useCallback(\n    () => (onBoard ? onBoard() : openLanding(\"board\")),\n    [onBoard, openLanding],\n  );\n  const handleGetToken = React.useCallback(() => {\n    if (onGetToken) return onGetToken();\n    window.open(oneUsdBridgeUrl(env), \"_blank\", \"noopener,noreferrer\");\n  }, [onGetToken, env]);\n\n  const claimableTotal = React.useMemo(() => {\n    if (!summary || claimableCount <= 0) return null;\n    return formatAmount2(\n      summary.claimableTotalRaw,\n      summary.claimableDecimals ?? 18,\n    );\n  }, [summary, claimableCount]);\n\n  const recentActivity = React.useMemo(\n    () => activity.slice(0, RECENT_ACTIVITY_LIMIT),\n    [activity],\n  );\n\n  const handleImageError = React.useCallback(\n    (e: React.SyntheticEvent<HTMLImageElement>) => {\n      e.currentTarget.style.display = \"none\";\n    },\n    [],\n  );\n\n  return (\n    <>\n      <div className=\"wi-sticky-header\">\n        <div className=\"wi-header wi-header--receive\">\n          <button\n            type=\"button\"\n            className=\"wi-header-back\"\n            onClick={onBack}\n            aria-label=\"Back\"\n          >\n            <BackIcon size={24} />\n          </button>\n          <span className=\"wi-op-header-title\">\n            {logoSrc ? (\n              <img\n                src={logoSrc}\n                alt=\"ONEpop\"\n                className=\"wi-op-logo-img\"\n                height={24}\n                draggable={false}\n                onError={handleImageError}\n              />\n            ) : (\n              <OnePopLogo className=\"wi-op-logo\" />\n            )}\n          </span>\n          <button\n            type=\"button\"\n            className=\"wi-header-action\"\n            onClick={onClose}\n            aria-label=\"Close\"\n          >\n            <CloseIcon size={24} />\n          </button>\n        </div>\n        <hr className=\"wi-divider\" />\n      </div>\n\n      <div className=\"wi-op-scroll\" data-vaul-no-drag>\n        <section className=\"wi-op-hero\">\n          <div className=\"wi-op-hero-copy\">\n            <h2 className=\"wi-op-hero-title\">\n              <span>Send money with</span>\n              <span className=\"wi-op-hero-title-row\">\n                just an\n                <XBadgeIcon size={20} className=\"wi-op-x-badge\" />\n                @handle.\n              </span>\n            </h2>\n            <p className=\"wi-op-hero-sub\">\n              They don&apos;t even need a wallet yet.\n            </p>\n          </div>\n          <div className=\"wi-op-hero-art\" aria-hidden>\n            {heroSrc ? (\n              <img\n                src={heroSrc}\n                alt=\"\"\n                className=\"wi-op-hero-img\"\n                draggable={false}\n                onError={handleImageError}\n              />\n            ) : (\n              <OnePopHeroArt />\n            )}\n          </div>\n        </section>\n\n        <section className=\"wi-op-panel\">\n          <div className=\"wi-op-balance-card\">\n            <div className=\"wi-op-balance-head\">\n              <span className=\"wi-op-balance-label\">Available</span>\n              {getTokenSlot ?? (\n                <button\n                  type=\"button\"\n                  className=\"wi-op-link-btn\"\n                  onClick={handleGetToken}\n                  data-track=\"onepop_get_token\"\n                >\n                  <span>Get ONEUSD</span>\n                  <OnePopChevronRightIcon size={12} />\n                </button>\n              )}\n            </div>\n            <div className=\"wi-op-balance-body\">\n              {isBalanceLoading ? (\n                <div\n                  className=\"wi-skeleton-block\"\n                  style={{ width: 140, height: 32 }}\n                />\n              ) : (\n                <>\n                  {balanceIconUrl && (\n                    <img\n                      src={balanceIconUrl}\n                      alt=\"\"\n                      aria-hidden\n                      className=\"wi-op-balance-icon\"\n                      width={24}\n                      height={24}\n                      draggable={false}\n                      onError={handleImageError}\n                    />\n                  )}\n                  <span className=\"wi-op-balance-value\">\n                    {balanceDisplay ?? \"0.00\"}\n                  </span>\n                </>\n              )}\n            </div>\n          </div>\n\n          <div className=\"wi-op-action-row\" aria-label=\"ONEpop actions\">\n            <button\n              type=\"button\"\n              className=\"wi-op-action-card\"\n              onClick={handleSend}\n              aria-label=\"Send POP\"\n              data-track=\"onepop_send\"\n            >\n              <OnePopSendIcon size={24} />\n              <span>Send POP</span>\n            </button>\n            <button\n              type=\"button\"\n              className=\"wi-op-action-card\"\n              onClick={handleClaim}\n              aria-label=\"Claim POPs\"\n              data-track=\"onepop_claim\"\n            >\n              <OnePopClaimIcon size={24} />\n              <span>Claim POPs</span>\n            </button>\n            {/* 수신 내역은 X 핸들 기준이라 X 연결이 있어야 의미가 있다. */}\n            {xConnected && (\n              <button\n                type=\"button\"\n                className=\"wi-op-action-card\"\n                onClick={handleActivity}\n                aria-label=\"Activity\"\n                data-track=\"onepop_activity\"\n              >\n                <OnePopActivityIcon size={24} />\n                <span>Activity</span>\n              </button>\n            )}\n          </div>\n        </section>\n\n        <section className=\"wi-op-cta-row\">\n          {isLoading ? (\n            <div\n              className=\"wi-skeleton-block wi-op-cta-skeleton\"\n              style={{ height: 40 }}\n            />\n          ) : (\n            <>\n              {/* 내역이 전혀 없으면 온보딩 CTA 두 개(보내기 유도 + 받은 게\n                  있을지도 모른다는 안내)를 함께 노출한다. */}\n              {!hasActivity && claimableCount <= 0 && (\n                <button\n                  type=\"button\"\n                  className=\"wi-op-cta wi-op-cta--gradient\"\n                  onClick={handleSend}\n                  data-track=\"onepop_send_first\"\n                >\n                  Send your first POP!\n                </button>\n              )}\n              {claimableCount > 0 ? (\n                <button\n                  type=\"button\"\n                  className=\"wi-op-cta wi-op-cta--purple\"\n                  onClick={handleClaim}\n                  data-track=\"onepop_claim_banner\"\n                >\n                  {`${claimableCount} POP${claimableCount === 1 ? \"\" : \"s\"} waiting to claim (total ${claimableTotal})`}\n                </button>\n              ) : (\n                !hasActivity && (\n                  // 아직 아무 내역이 없는 사용자는 공개 보드로 보내\n                  // \"받은 게 있을지도\" 를 직접 확인하게 한다.\n                  <button\n                    type=\"button\"\n                    className=\"wi-op-cta wi-op-cta--purple\"\n                    onClick={handleBoard}\n                    data-track=\"onepop_claim_maybe\"\n                  >\n                    You might have POPs waiting\n                  </button>\n                )\n              )}\n              {repliesWaitingCount > 0 && (\n                <button\n                  type=\"button\"\n                  className=\"wi-op-cta wi-op-cta--outline\"\n                  onClick={handleActivity}\n                  data-track=\"onepop_replies\"\n                >\n                  {`${repliesWaitingCount} ${repliesWaitingCount === 1 ? \"reply\" : \"replies\"} waiting`}\n                </button>\n              )}\n            </>\n          )}\n        </section>\n\n        {hasActivity ? (\n          <section className=\"wi-op-activity\">\n            <div className=\"wi-op-activity-head\">\n              <div className=\"wi-op-activity-heading\">\n                <span className=\"wi-op-activity-title\">Recent activity</span>\n                <span className=\"wi-op-activity-currency\">\n                  Currency: ONEUSD\n                </span>\n              </div>\n              <button\n                type=\"button\"\n                className=\"wi-op-link-btn\"\n                onClick={handleActivity}\n                data-track=\"onepop_see_all_activity\"\n              >\n                <span>See all activity</span>\n                <OnePopChevronRightIcon size={16} />\n              </button>\n            </div>\n            <ul className=\"wi-op-activity-list\">\n              {recentActivity.map((item) => (\n                <ActivityRow key={item.id} item={item} />\n              ))}\n            </ul>\n          </section>\n        ) : (\n          <section className=\"wi-op-features\">\n            <FeatureRow\n              icon={<OnePopHandleIcon size={20} />}\n              title=\"No wallet address needed\"\n              desc=\"just their @handle\"\n            />\n            <FeatureRow\n              icon={<OnePopInstantIcon size={20} />}\n              title=\"Claim instantly\"\n              desc=\"social login, zero gas\"\n            />\n            <FeatureRow\n              icon={<OnePopRefundIcon size={20} />}\n              title=\"Refund anytime\"\n              desc=\"get it back if unclaimed\"\n            />\n          </section>\n        )}\n      </div>\n\n      <div className=\"wi-op-footer\">\n        <span>Powered by ONEpop</span>\n      </div>\n    </>\n  );\n}\n\nfunction FeatureRow({\n  icon,\n  title,\n  desc,\n}: {\n  icon: React.ReactNode;\n  title: string;\n  desc: string;\n}) {\n  return (\n    <div className=\"wi-op-feature\">\n      <span className=\"wi-op-feature-icon\" aria-hidden>\n        {icon}\n      </span>\n      <span className=\"wi-op-feature-copy\">\n        <span className=\"wi-op-feature-title\">{title}</span>\n        <span className=\"wi-op-feature-desc\">{desc}</span>\n      </span>\n    </div>\n  );\n}\n\nfunction ActivityRow({ item }: { item: OnePopActivityItem }) {\n  const handle = withAt(item.handle);\n  const label =\n    item.direction === \"sent\" ? `Sent to ${handle}` : `Claimed from ${handle}`;\n  const initial = handle.replace(\"@\", \"\").charAt(0).toUpperCase() || \"?\";\n\n  return (\n    <li className=\"wi-op-activity-item\">\n      <span className=\"wi-op-activity-avatar\" aria-hidden>\n        {item.avatarUrl ? (\n          <img\n            src={item.avatarUrl}\n            alt=\"\"\n            width={30}\n            height={30}\n            draggable={false}\n            onError={(e) => {\n              e.currentTarget.style.display = \"none\";\n            }}\n          />\n        ) : (\n          <span className=\"wi-op-activity-initial\">{initial}</span>\n        )}\n      </span>\n      <span className=\"wi-op-activity-copy\">\n        <span className=\"wi-op-activity-meta\">\n          <span className=\"wi-op-activity-peer\">{label}</span>\n          <span className=\"wi-op-activity-sep\" aria-hidden />\n          <span className=\"wi-op-activity-time\">\n            {formatRelativeTime(item.timestamp)}\n          </span>\n        </span>\n        <span className=\"wi-op-activity-amount\">\n          {formatAmount2(item.amountRaw, item.decimals)}\n        </span>\n      </span>\n      <span\n        className={cn(\"wi-op-badge\", `wi-op-badge--${item.status}`)}\n        data-status={item.status}\n      >\n        {STATUS_LABEL[item.status]}\n      </span>\n    </li>\n  );\n}\n","import type { Environment } from \"../../types\";\n\n/**\n * ONEpop 서비스 웹의 랜딩 페이지. 화면의 버튼/배너는 전부 이 페이지들로의\n * 이동이라 disabled 상태가 없다 — 노출 여부만 API 응답으로 정한다.\n *\n * 공개 랜딩은 `send`(루트)와 `board`뿐이고 `claim`/`activity`는 서비스 안에서\n * 로그인(X 연결)을 요구하는 noindex 페이지지만, 접근 제어는 서비스 몫이므로\n * 여기서는 단순 딥링크만 만든다.\n */\nexport type OnePopLandingPage = \"send\" | \"claim\" | \"activity\" | \"board\";\n\nconst ONE_POP_WEB_BASE: Record<Environment, string> = {\n  dev: \"https://dev.onechain.nexus/pop\",\n  stage: \"https://stg.onechain.nexus/pop\",\n  production: \"https://onechain.nexus/pop\",\n};\n\nexport function onePopLandingUrl(\n  env: Environment,\n  page: OnePopLandingPage,\n): string {\n  const base = ONE_POP_WEB_BASE[env] ?? ONE_POP_WEB_BASE.production;\n  return page === \"send\" ? base : `${base}/${page}`;\n}\n\n/**\n * \"Get ONEUSD ›\" 의 목적지 — 게임토큰 웹의 브리지 페이지. 게임토큰은 dev\n * 배포가 없어 dev 도 stage 를 본다(앱런처 메뉴의 gametoken 항목과 동일 규칙).\n */\nconst ONE_USD_BRIDGE_URL: Record<Environment, string> = {\n  dev: \"https://stg.onechain.nexus/gametoken/bridge\",\n  stage: \"https://stg.onechain.nexus/gametoken/bridge\",\n  production: \"https://www.onechain.nexus/gametoken/bridge\",\n};\n\nexport function oneUsdBridgeUrl(env: Environment): string {\n  return ONE_USD_BRIDGE_URL[env] ?? ONE_USD_BRIDGE_URL.production;\n}\n","/**\n * ONEpop 화면 전용 아이콘. wallet-info/components/icons.tsx 와 동일한 규약\n * (`size` prop + `currentColor` stroke)을 따른다.\n *\n * 브랜드 아트워크(로고 워드마크 / 히어로 일러스트)는 Figma 원본이 래스터라\n * 여기서는 인라인 SVG 재현본을 둔다. 실제 에셋이 CDN에 올라가면\n * `OnePopBody`의 `logoSrc` / `heroSrc` prop 으로 덮어쓸 수 있다.\n */\n\ninterface IconProps {\n  size?: number;\n  className?: string;\n}\n\n/**\n * 액션 행 / 진입 버튼용 하트. Figma 원본(`heart.svg`)이며 하트 안에 숫자 1이\n * 들어간 ONE 브랜드 마크다 — 이전엔 lucide/heart 를 그대로 썼다.\n *\n * 원본에서 한 가지만 조정했다. `stroke=\"white\" strokeOpacity=\"0.65\"` →\n * `currentColor`. Figma 는 다크 배경 기준으로 내보내서 흰색이 박혀 있는데,\n * 하드코딩하면 라이트 테마에서 사라진다. 색은 `.wi-action-card` 가\n * `--ds-content-high` 로 준다. 0.65 도 같이 뺐다 — 같은 행의 Receive/Send\n * 아이콘은 불투명이라 그것만 흐려 보인다.\n */\nexport function OnePopActionIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M1.66675 7.91662C1.66677 6.98929 1.94808 6.08377 2.47353 5.31967C2.99898 4.55557 3.74385 3.96883 4.60976 3.63695C5.47567 3.30507 6.42188 3.24366 7.32343 3.46082C8.22497 3.67799 9.03944 4.16352 9.65925 4.85329C9.7029 4.89996 9.75568 4.93718 9.81431 4.96262C9.87294 4.98806 9.93617 5.00119 10.0001 5.00119C10.064 5.00119 10.1272 4.98806 10.1859 4.96262C10.2445 4.93718 10.2973 4.89996 10.3409 4.85329C10.9588 4.15904 11.7734 3.66943 12.6764 3.44962C13.5795 3.22982 14.528 3.29024 15.3958 3.62286C16.2636 3.95547 17.0096 4.5445 17.5343 5.31154C18.0591 6.07858 18.3378 6.98725 18.3334 7.91662C18.3334 9.82495 17.0834 11.25 15.8334 12.5L11.2567 16.9275C11.1015 17.1058 10.91 17.249 10.6951 17.3477C10.4802 17.4464 10.2468 17.4982 10.0103 17.4997C9.77386 17.5012 9.53979 17.4523 9.32365 17.3564C9.10752 17.2605 8.91427 17.1196 8.75675 16.9433L4.16675 12.5C2.91675 11.25 1.66675 9.83329 1.66675 7.91662Z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.25}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M8.84839 8.28833H10V13.4938\"\n        stroke=\"currentColor\"\n        strokeWidth={1.25}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n/** Send POP (lucide/send). */\nexport function OnePopSendIcon({ size = 24, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z\"\n        stroke=\"currentColor\"\n        strokeWidth={1.6}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"m21.854 2.147-10.94 10.939\"\n        stroke=\"currentColor\"\n        strokeWidth={1.6}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n/** Claim POPs (lucide/hand-coins). */\nexport function OnePopClaimIcon({ size = 24, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <g\n        stroke=\"currentColor\"\n        strokeWidth={1.6}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <path d=\"M11 15h2a2 2 0 1 0 0-4h-3c-.6 0-1.1.2-1.4.6L3 17\" />\n        <path d=\"m7 21 1.6-1.4c.3-.4.8-.6 1.4-.6h4c1.1 0 2.1-.4 2.8-1.2l4.6-4.4a2 2 0 0 0-2.75-2.91l-4.2 3.9\" />\n        <path d=\"m2 16 6 6\" />\n        <circle cx=\"16\" cy=\"9\" r=\"2.9\" />\n        <circle cx=\"6\" cy=\"5\" r=\"3\" />\n      </g>\n    </svg>\n  );\n}\n\n/** Activity (lucide/scroll-text). */\nexport function OnePopActivityIcon({ size = 24, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <g\n        stroke=\"currentColor\"\n        strokeWidth={1.6}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <path d=\"M15 12h-5M15 8h-5\" />\n        <path d=\"M19 17V5a2 2 0 0 0-2-2H4\" />\n        <path d=\"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3\" />\n      </g>\n    </svg>\n  );\n}\n\n/** \"Get ONEUSD ›\" / \"See all activity ›\" 의 꺾쇠. */\nexport function OnePopChevronRightIcon({ size = 12, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <path\n        d=\"m9 18 6-6-6-6\"\n        stroke=\"currentColor\"\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\n/** 히어로 문구 안의 X(구 트위터) 배지. */\nexport function XBadgeIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 20 20\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <rect width=\"20\" height=\"20\" rx=\"4.8\" fill=\"currentColor\" />\n      <path\n        d=\"M11.53 9.05 15.7 4.4h-.99l-3.62 4.04L8.2 4.4H4.86l4.37 6.1-4.37 4.87h.99l3.82-4.26 3.05 4.26h3.34zm-1.35 1.51-.44-.61-3.52-4.9h1.52l2.84 3.96.44.61 3.7 5.14h-1.52z\"\n        fill=\"var(--wi-op-x-glyph, #ffffff)\"\n      />\n    </svg>\n  );\n}\n\n/** 빈 상태 항목 1 — \"No wallet address needed\". */\nexport function OnePopHandleIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <g\n        stroke=\"currentColor\"\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <circle cx=\"9\" cy=\"7\" r=\"3.4\" />\n        <path d=\"M2.6 20.4a6.4 6.4 0 0 1 10.2-5.15\" />\n        <circle cx=\"17.5\" cy=\"17.5\" r=\"2.1\" />\n        <path d=\"M19.6 17.5v1.05a1.55 1.55 0 0 0 3.1 0v-1.05a5.2 5.2 0 1 0-2.05 4.14\" />\n      </g>\n    </svg>\n  );\n}\n\n/** 빈 상태 항목 2 — \"Claim instantly\". */\nexport function OnePopInstantIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <g\n        stroke=\"currentColor\"\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <path d=\"M12.5 3 5 13.5h5.5L10 21l7.5-10.5H12z\" />\n        <path d=\"M20 4.5 21.5 3M20.5 8h2M17 2.5V1\" />\n      </g>\n    </svg>\n  );\n}\n\n/** 빈 상태 항목 3 — \"Refund anytime\". */\nexport function OnePopRefundIcon({ size = 20, className }: IconProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className={className}\n    >\n      <g\n        stroke=\"currentColor\"\n        strokeWidth={1.7}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      >\n        <rect x=\"2.5\" y=\"5.5\" width=\"19\" height=\"13\" rx=\"2.5\" />\n        <path d=\"M12 9v6\" />\n        <path d=\"m9.5 12.5 2.5 2.5 2.5-2.5\" />\n      </g>\n    </svg>\n  );\n}\n\n/**\n * ONEpop 워드마크 — 하트(숫자 1) 마크 + \"ONEpop\" 레터폼. Figma 브랜드 에셋\n * 원본이며, 재현본이 아니므로 임의로 좌표를 손대지 않는다. 실제 이미지 파일로\n * 바꾸려면 `OnePopBody` 의 `logoSrc` 를 쓴다.\n *\n * 원본에서 두 가지만 조정했다.\n * - 레터폼 `fill` 을 `black` → `currentColor`. 하드코딩하면 다크 테마에서\n *   배경과 붙는다. 색은 `.wi-op-logo` 가 `--ds-content-highest` 로 준다.\n * - gradient/mask id 에 `wi-op-logo-` 접두사. Figma 내보내기의\n *   `paint0_linear_6126_2359` 같은 id 는 문서 전역이라 다른 내보내기와 충돌할\n *   수 있다. 마스크는 alpha 타입이고 원본 그라디언트가 전부 불투명이라\n *   `white` 로 눌러 중복 정의(`paint1`) 하나를 없앴다.\n *\n * 마크의 주황→핑크 그라디언트는 브랜드 고정이라 테마 토큰을 쓰지 않는다\n * (`Send your first POP!` 배경과 같은 취급).\n */\nexport function OnePopLogo({ className }: { className?: string }) {\n  return (\n    <svg\n      width=\"116\"\n      height=\"24\"\n      viewBox=\"0 0 116 24\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      // 레터폼이 path 라 텍스트 노드가 없다 — 이름을 명시하지 않으면 헤더\n      // 타이틀이 스크린리더에서 사라진다.\n      role=\"img\"\n      aria-label=\"ONEpop\"\n      className={className}\n    >\n      <path\n        d=\"M38.1949 4.06787C33.7184 4.06787 30.085 7.69479 30.085 12.1647C30.085 16.6346 33.7184 20.2616 38.1949 20.2616C42.6713 20.2616 46.3048 16.6346 46.3048 12.1647C46.3048 7.69479 42.6713 4.06787 38.1949 4.06787ZM38.1949 16.6216C35.7312 16.6216 33.7315 14.6284 33.7315 12.1647C33.7315 9.70103 35.7312 7.70786 38.1949 7.70786C40.6586 7.70786 42.6583 9.70103 42.6583 12.1647C42.6583 14.6284 40.6586 16.6216 38.1949 16.6216Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M52.0033 4.81283C51.6438 4.40113 51.1537 4.11359 50.5721 4.11359C49.5657 4.11359 48.7554 4.93046 48.7554 5.93031V18.4513C48.7554 19.4577 49.5722 20.2681 50.5721 20.2681C51.5719 20.2681 52.3888 19.4512 52.3888 18.4513V10.7727L60.5837 19.5492C60.9431 19.9609 61.4332 20.2485 62.0148 20.2485C63.0212 20.2485 63.8316 19.4316 63.8316 18.4317C63.8316 17.9351 63.6094 17.5038 63.3153 17.1639L51.9967 4.81936L52.0033 4.81283Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M61.8643 12.3934C62.9034 12.3934 63.7398 11.5504 63.7398 10.5179V5.96953C63.7398 4.93047 62.8968 4.09399 61.8643 4.09399C60.8318 4.09399 59.9888 4.93701 59.9888 5.96953V10.5179C59.9888 11.5569 60.8318 12.3934 61.8643 12.3934Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M76.9017 7.74704C77.9081 7.74704 78.7249 6.93016 78.7249 5.93031C78.7249 4.93046 77.9081 4.11359 76.9017 4.11359H68.5108C67.5044 4.11359 66.6875 4.93046 66.6875 5.93031V18.4513C66.6875 19.4577 67.5044 20.2681 68.5108 20.2681H76.9017C77.9081 20.2681 78.7249 19.4512 78.7249 18.4513C78.7249 17.4515 77.9081 16.6346 76.9017 16.6346H70.3275V14.0075H76.2286C77.235 14.0075 78.0518 13.1907 78.0518 12.1908C78.0518 11.191 77.235 10.3741 76.2286 10.3741H70.3275V7.74704H76.9017Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M84.5951 23.1908C84.3206 23.1908 84.105 23.1124 83.9481 22.9555C83.7913 22.7987 83.7129 22.5765 83.7129 22.2889V10.7416C83.7129 10.441 83.7913 10.2188 83.9481 10.0751C84.105 9.91823 84.3206 9.83981 84.5951 9.83981C84.8827 9.83981 85.0983 9.91823 85.2421 10.0751C85.3989 10.2188 85.4773 10.441 85.4773 10.7416V12.5257L85.2617 12.1728C85.4708 11.4539 85.8825 10.8789 86.4968 10.4476C87.1111 10.0163 87.8495 9.8006 88.7122 9.8006C89.5617 9.8006 90.3002 10.0032 90.9275 10.4084C91.5549 10.8004 92.045 11.369 92.3979 12.114C92.7508 12.8459 92.9272 13.7216 92.9272 14.741C92.9272 15.7474 92.7508 16.6297 92.3979 17.3877C92.0581 18.1327 91.5679 18.7078 90.9275 19.1129C90.3002 19.505 89.5617 19.7011 88.7122 19.7011C87.8626 19.7011 87.1307 19.4854 86.5164 19.0541C85.9021 18.6228 85.4904 18.0543 85.2813 17.3485H85.4969V22.2889C85.4969 22.5765 85.4185 22.7987 85.2617 22.9555C85.1048 23.1124 84.8827 23.1908 84.5951 23.1908ZM88.3004 18.2895C88.8625 18.2895 89.3526 18.1523 89.7708 17.8778C90.2021 17.6034 90.5354 17.2047 90.7707 16.6819C91.0059 16.1591 91.1236 15.5122 91.1236 14.741C91.1236 13.5909 90.8687 12.7152 90.359 12.114C89.8492 11.5128 89.1631 11.2122 88.3004 11.2122C87.7254 11.2122 87.2222 11.3494 86.7909 11.6239C86.3726 11.8853 86.0459 12.2774 85.8106 12.8002C85.5884 13.323 85.4773 13.9699 85.4773 14.741C85.4773 15.8912 85.7322 16.7734 86.2419 17.3877C86.7517 17.9889 87.4378 18.2895 88.3004 18.2895Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M99.2775 19.7011C98.3365 19.7011 97.5196 19.4985 96.8269 19.0933C96.1342 18.6882 95.5983 18.1131 95.2193 17.3681C94.8403 16.6231 94.6508 15.7474 94.6508 14.741C94.6508 13.983 94.7553 13.3033 94.9644 12.7021C95.1866 12.0878 95.5003 11.565 95.9055 11.1337C96.3106 10.7024 96.7942 10.3757 97.3562 10.1535C97.9313 9.91823 98.5718 9.8006 99.2775 9.8006C100.219 9.8006 101.035 10.0032 101.728 10.4084C102.421 10.8135 102.957 11.3886 103.336 12.1336C103.715 12.8655 103.904 13.7347 103.904 14.741C103.904 15.4991 103.793 16.1853 103.571 16.7996C103.362 17.4139 103.055 17.9366 102.65 18.368C102.244 18.7993 101.754 19.1326 101.179 19.3678C100.617 19.59 99.9833 19.7011 99.2775 19.7011ZM99.2775 18.2895C99.8526 18.2895 100.349 18.1523 100.768 17.8778C101.199 17.6034 101.526 17.2047 101.748 16.6819C101.983 16.1591 102.101 15.5122 102.101 14.741C102.101 13.5909 101.846 12.7152 101.336 12.114C100.826 11.5128 100.14 11.2122 99.2775 11.2122C98.7155 11.2122 98.2189 11.3494 97.7876 11.6239C97.3693 11.8853 97.0426 12.2774 96.8073 12.8002C96.572 13.323 96.4544 13.9699 96.4544 14.741C96.4544 15.8912 96.7093 16.7734 97.219 17.3877C97.7418 17.9889 98.428 18.2895 99.2775 18.2895Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M107.072 23.1908C106.797 23.1908 106.582 23.1124 106.425 22.9555C106.268 22.7987 106.19 22.5765 106.19 22.2889V10.7416C106.19 10.441 106.268 10.2188 106.425 10.0751C106.582 9.91823 106.797 9.83981 107.072 9.83981C107.359 9.83981 107.575 9.91823 107.719 10.0751C107.876 10.2188 107.954 10.441 107.954 10.7416V12.5257L107.738 12.1728C107.948 11.4539 108.359 10.8789 108.974 10.4476C109.588 10.0163 110.326 9.8006 111.189 9.8006C112.038 9.8006 112.777 10.0032 113.404 10.4084C114.032 10.8004 114.522 11.369 114.875 12.114C115.228 12.8459 115.404 13.7216 115.404 14.741C115.404 15.7474 115.228 16.6297 114.875 17.3877C114.535 18.1327 114.045 18.7078 113.404 19.1129C112.777 19.505 112.038 19.7011 111.189 19.7011C110.339 19.7011 109.607 19.4854 108.993 19.0541C108.379 18.6228 107.967 18.0543 107.758 17.3485H107.974V22.2889C107.974 22.5765 107.895 22.7987 107.738 22.9555C107.582 23.1124 107.359 23.1908 107.072 23.1908ZM110.777 18.2895C111.339 18.2895 111.829 18.1523 112.248 17.8778C112.679 17.6034 113.012 17.2047 113.247 16.6819C113.483 16.1591 113.6 15.5122 113.6 14.741C113.6 13.5909 113.345 12.7152 112.836 12.114C112.326 11.5128 111.64 11.2122 110.777 11.2122C110.202 11.2122 109.699 11.3494 109.268 11.6239C108.849 11.8853 108.523 12.2774 108.287 12.8002C108.065 13.323 107.954 13.9699 107.954 14.741C107.954 15.8912 108.209 16.7734 108.719 17.3877C109.228 17.9889 109.915 18.2895 110.777 18.2895Z\"\n        fill=\"currentColor\"\n      />\n      <path\n        d=\"M14.7326 0.727918C12.9726 1.59329 11.6096 4.00482 11.0833 5.59357C9.88384 4.95085 7.89924 3.76837 5.7791 4.00482C3.25593 4.28622 1.16093 5.72223 0.333058 8.59505C-0.616745 11.891 0.425001 15.7904 3.97287 17.5788C7.25977 19.2357 10.3262 21.1047 11.8468 22.2197C13.3673 23.3348 14.6332 24.4164 15.5435 23.8398C17.1655 23.054 20.2401 19.6295 22.4534 16.1013C24.3507 13.0769 25.9439 7.65453 23.3488 3.26211C21.2608 -0.0620856 17.6891 -0.725834 14.7326 0.727918Z\"\n        fill=\"url(#wi-op-logo-heart)\"\n      />\n      <mask\n        id=\"wi-op-logo-mask\"\n        style={{ maskType: \"alpha\" }}\n        maskUnits=\"userSpaceOnUse\"\n        x=\"0\"\n        y=\"0\"\n        width=\"25\"\n        height=\"24\"\n      >\n        <path\n          d=\"M14.7326 0.727918C12.9726 1.59329 11.6096 4.00482 11.0833 5.59357C9.88384 4.95085 7.89924 3.76837 5.7791 4.00482C3.25593 4.28622 1.16093 5.72223 0.333058 8.59505C-0.616745 11.891 0.425001 15.7904 3.97287 17.5788C7.25977 19.2357 10.3262 21.1047 11.8468 22.2197C13.3673 23.3348 14.6332 24.4164 15.5435 23.8398C17.1655 23.054 20.2401 19.6295 22.4534 16.1013C24.3507 13.0769 25.9439 7.65453 23.3488 3.26211C21.2608 -0.0620856 17.6891 -0.725834 14.7326 0.727918Z\"\n          fill=\"white\"\n        />\n      </mask>\n      <g mask=\"url(#wi-op-logo-mask)\">\n        <path\n          d=\"M29.5305 -3.85553H-1.43018V24.7597H29.5305V-3.85553Z\"\n          fill=\"url(#wi-op-logo-base)\"\n        />\n        <path\n          d=\"M12.7993 24.7597H29.5372V1.58752L12.7993 13.5165V24.7597Z\"\n          fill=\"url(#wi-op-logo-shine-r)\"\n        />\n        <path\n          d=\"M12.8037 24.7598H-1.63916V2.94476L12.8037 13.5166V24.7598Z\"\n          fill=\"url(#wi-op-logo-shine-l)\"\n        />\n      </g>\n      <path\n        d=\"M13.4339 7.58409L9.79429 9.73601C9.1523 10.1136 8.92988 10.962 9.2989 11.6189C9.66791 12.2759 10.4969 12.5035 11.1389 12.1259L14.2174 10.305L12.7515 11.9138V18.2247C12.7515 18.9852 13.3581 19.6059 14.1012 19.6059C14.8442 19.6059 15.4508 18.9852 15.4508 18.2247V8.77903C15.4508 8.51521 15.37 8.25656 15.2689 8.08586C14.8897 7.43407 14.0708 7.20129 13.4288 7.57891L13.4339 7.58409Z\"\n        fill=\"white\"\n      />\n      <defs>\n        <linearGradient\n          id=\"wi-op-logo-heart\"\n          x1=\"12.3679\"\n          y1=\"-0.18782\"\n          x2=\"12.3679\"\n          y2=\"23.9797\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"#F1603C\" />\n          <stop offset=\"1\" stopColor=\"#EE2566\" />\n        </linearGradient>\n        <linearGradient\n          id=\"wi-op-logo-base\"\n          x1=\"-5.01624\"\n          y1=\"-7.30565\"\n          x2=\"28.6827\"\n          y2=\"29.4172\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"#F3752D\" />\n          <stop offset=\"1\" stopColor=\"#EC1272\" />\n        </linearGradient>\n        <linearGradient\n          id=\"wi-op-logo-shine-r\"\n          x1=\"10.2312\"\n          y1=\"2.71613\"\n          x2=\"23.8708\"\n          y2=\"13.2539\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"white\" stopOpacity=\"0.5\" />\n          <stop offset=\"1\" stopColor=\"white\" stopOpacity=\"0\" />\n        </linearGradient>\n        <linearGradient\n          id=\"wi-op-logo-shine-l\"\n          x1=\"24.1414\"\n          y1=\"-2.87687\"\n          x2=\"12.838\"\n          y2=\"18.379\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"white\" stopOpacity=\"0.5\" />\n          <stop offset=\"1\" stopColor=\"white\" stopOpacity=\"0\" />\n        </linearGradient>\n      </defs>\n    </svg>\n  );\n}\n\n/**\n * 히어로 일러스트 원본 데이터. Figma 내보내기(`Rectangle.svg`)는 이름만 SVG 고\n * 실제로는 1600×1410 PNG(735KB)를 감싼 래퍼였다. 이 패키지는 `dist` 만\n * 배포해서 에셋 파이프라인이 없으므로 인라인 말고는 방법이 없고, 735KB 를 그대로\n * base64 로 넣을 수는 없다. 실제 렌더 크기가 82×72 CSS px 이라 3x(246px)로 줄이고\n * WebP q90 으로 다시 압축했다 — 8KB(base64 10.6KB).\n */\nconst ONEPOP_HERO_ART_SRC =\n  \"data:image/webp;base64,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\";\n\n/**\n * 히어로 일러스트 — 하트를 받쳐 든 손. Figma 원본 래스터를 인라인한 것이며,\n * `OnePopBody` 의 `heroSrc` 로 다른 이미지를 주입해 덮어쓸 수 있다.\n *\n * 이전 재현본은 벡터라 `--wi-op-hero-line` 으로 아웃라인을 다크 테마에서 흰색으로\n * 뒤집었다. 래스터에는 그게 안 먹으므로 아웃라인은 어느 테마에서나 원본의 검정\n * 그대로다 — 흰 장갑 + 빨간 하트가 실루엣을 지고 있어 어두운 배경에서도 형태는\n * 읽힌다. 테마별로 갈라야 하면 이미지가 두 장 필요하다.\n *\n * `<img>` 가 아니라 `<svg><image>` 인 이유는 82×72 고정 크기를 CSS 없이 그 자체로\n * 갖게 하려는 것이다 — `.wi-op-hero-art svg` 를 잡는 소비자 CSS 도 그대로 산다.\n */\nexport function OnePopHeroArt({ className }: { className?: string }) {\n  return (\n    <svg\n      width=\"82\"\n      height=\"72\"\n      viewBox=\"0 0 82 72\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden\n      className={className}\n    >\n      <image\n        href={ONEPOP_HERO_ART_SRC}\n        width=\"82\"\n        height=\"72\"\n        preserveAspectRatio=\"xMidYMid meet\"\n      />\n    </svg>\n  );\n}\n","import styleInject from '#style-inject';styleInject(\".wi-op-scroll{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;display:flex;flex-direction:column;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.15) transparent}[data-theme=light] .wi-op-scroll{scrollbar-color:rgba(0,0,0,.12) transparent}.wi-op-scroll::-webkit-scrollbar{width:4px}.wi-op-scroll::-webkit-scrollbar-track{background:transparent}.wi-op-scroll::-webkit-scrollbar-thumb{background:#ffffff26;border-radius:4px}[data-theme=light] .wi-op-scroll::-webkit-scrollbar-thumb{background:#0000001f}.wi-op-header-title{position:absolute;left:50%;transform:translate(-50%);display:inline-flex;align-items:center;pointer-events:none}.wi-op-logo{display:block;flex-shrink:0;color:var(--ds-content-highest, #000000)}.wi-op-logo-img{height:24px;width:auto;object-fit:contain}.wi-op-hero{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:24px;flex-shrink:0}.wi-op-hero-copy{display:flex;flex-direction:column;gap:6px;min-width:0}.wi-op-hero-title{display:flex;flex-direction:column;margin:0;font-family:inherit;font-size:18px;font-weight:600;line-height:1.25;color:var(--ds-content-highest, #000000)}.wi-op-hero-title-row{display:inline-flex;align-items:center;gap:4px;flex-wrap:wrap}.wi-op-x-badge{flex-shrink:0;color:var(--ds-content-highest, #000000)}[data-theme=light] .wi-op-x-badge{--wi-op-x-glyph: #ffffff}[data-theme=dark] .wi-op-x-badge{--wi-op-x-glyph: #1e232e}.wi-op-hero-sub{margin:0;font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-op-hero-art{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:120px;height:72px}.wi-op-hero-img{max-width:100%;max-height:72px;object-fit:contain}.wi-op-panel{display:flex;flex-direction:column;gap:8px;padding:0 24px;flex-shrink:0}.wi-op-balance-card{display:flex;flex-direction:column;gap:8px;padding:12px;border-radius:12px;background:var(--ds-surface-medium, #f5f7fa)}.wi-op-balance-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.wi-op-balance-label{font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.wi-op-balance-body{display:flex;align-items:center;gap:4px;min-width:0}.wi-op-balance-icon{width:24px;height:24px;border-radius:50%;object-fit:cover;flex-shrink:0}.wi-op-balance-value{font-family:inherit;font-size:28px;font-weight:600;line-height:1.25;color:var(--ds-content-highest, #000000);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wi-op-link-btn{appearance:none;display:inline-flex;align-items:center;gap:4px;padding:0;border:none;background:transparent;font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer;flex-shrink:0;transition:color .15s ease}.wi-op-link-btn:hover:not(:disabled){color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-op-link-btn:disabled{cursor:default;opacity:.5}.wi-op-balance-head>[data-gou-mode=button]{display:contents}.wi-op-action-row{display:flex;align-items:stretch;gap:8px}.wi-op-action-card{appearance:none;display:flex;flex:1 1 0;min-width:0;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:10px 8px;border:none;border-radius:10px;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-content-medium, rgba(0, 0, 0, .65));font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;text-align:center;cursor:pointer;transition:background-color .15s ease,transform .1s ease}.wi-op-action-card>svg{flex-shrink:0}.wi-op-action-card:hover:not(:disabled){background:#2e3544}[data-theme=light] .wi-op-action-card:hover:not(:disabled){background:#e4e8ef}.wi-op-action-card:active:not(:disabled){transform:scale(.95)}.wi-op-action-card:disabled{cursor:default;opacity:.45}.wi-op-cta-row{display:flex;flex-direction:column;gap:10px;padding:24px;flex-shrink:0}.wi-op-cta-skeleton{border-radius:10px}.wi-op-cta{appearance:none;display:flex;align-items:center;justify-content:center;width:100%;padding:12px 16px;border:none;border-radius:10px;font-family:inherit;font-size:16px;font-weight:600;line-height:1;cursor:pointer;transition:filter .15s ease,transform .1s ease,background-color .15s ease}.wi-op-cta:active:not(:disabled){transform:scale(.985)}.wi-op-cta:disabled{cursor:default;opacity:.5}.wi-op-cta--gradient{background:linear-gradient(193.7deg,#f62e61 16.1%,#f7564a 83.9%);color:#fff}.wi-op-cta--gradient:hover:not(:disabled){filter:brightness(1.05)}.wi-op-cta--purple{background:var(--ds-system-purple-high, #7346f3);color:var(--ds-system-purple-on, #ffffff)}.wi-op-cta--purple:hover:not(:disabled){filter:brightness(1.08)}.wi-op-cta--outline{background:transparent;border:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-op-cta--outline:hover:not(:disabled){background:var(--ds-surface-medium, #f5f7fa)}.wi-op-features{display:flex;flex-direction:column;padding:0 24px 8px;flex-shrink:0}.wi-op-feature{display:flex;align-items:center;gap:6px;padding:12px 0}.wi-op-feature+.wi-op-feature{border-top:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wi-op-feature-icon{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:50%;flex-shrink:0;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-accent-primary-high, #f0640d)}.wi-op-feature-copy{display:flex;flex-direction:column;min-width:0}.wi-op-feature-title{font-family:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-op-feature-desc{font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-op-activity{display:flex;flex-direction:column;gap:8px;padding:0 24px 8px;flex-shrink:0}.wi-op-activity-head{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}.wi-op-activity-heading{display:flex;flex-direction:column;min-width:0}.wi-op-activity-title{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-op-activity-currency{font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-op-activity-list{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}.wi-op-activity-item{display:flex;align-items:flex-start;gap:6px;padding:12px 0}.wi-op-activity-item+.wi-op-activity-item{border-top:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wi-op-activity-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:50%;overflow:hidden;flex-shrink:0;background:var(--ds-surface-medium, #f5f7fa)}.wi-op-activity-avatar img{width:100%;height:100%;object-fit:cover}.wi-op-activity-initial{font-family:inherit;font-size:13px;font-weight:600;line-height:1;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.wi-op-activity-copy{display:flex;flex-direction:column;flex:1 1 0;min-width:0}.wi-op-activity-meta{display:flex;align-items:center;gap:6px;min-width:0}.wi-op-activity-peer{font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wi-op-activity-sep{width:1px;height:14px;flex-shrink:0;background:var(--ds-border-medium, rgba(0, 0, 0, .18))}.wi-op-activity-time{font-family:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));white-space:nowrap;flex-shrink:0}.wi-op-activity-amount{font-family:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.wi-op-badge{display:inline-flex;align-items:center;justify-content:center;padding:3px 6px;border-radius:4px;flex-shrink:0;font-family:inherit;font-size:12px;font-weight:500;line-height:1}.wi-op-badge--claimed{background:var(--ds-system-green-low, #e7f9f8);color:var(--ds-system-green-high, #00a0a2)}.wi-op-badge--waiting{background:var(--ds-accent-primary-low, #fff5ee);color:var(--ds-accent-primary-high, #f0640d)}.wi-op-badge--refunded{background:var(--ds-system-red-low, #ffeeee);color:var(--ds-system-red-high, #d20609)}.wi-op-badge--ready{background:var(--ds-system-blue-low, #ecf8fc);color:var(--ds-system-blue-high, #008dca)}.wi-op-footer{display:flex;align-items:center;justify-content:center;padding:16px 24px;flex-shrink:0;border-top:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-drawer-content .wi-op-hero,.wi-drawer-content .wi-op-cta-row,.wi-drawer-content .wi-op-panel,.wi-drawer-content .wi-op-features,.wi-drawer-content .wi-op-activity,.wi-drawer-content .wi-op-footer{padding-left:16px;padding-right:16px}\\n\")","\"use client\";\n\nimport * as React from \"react\";\nimport { OnePopChevronRightIcon } from \"./icons\";\nimport \"../styles/onepop.css\";\n\n/**\n * ONEpop Available 카드의 \"Get ONEUSD ›\" 링크를 그대로 재현한 트리거.\n *\n * `<GetOneUsd mode=\"button\" buttonComponent={OnePopGetTokenTrigger}>` 의\n * 커스텀 트리거로 쓰라고 있는 컴포넌트다 — 위젯이 넘기는 `onClick`/`disabled`/\n * `aria-label` 은 그대로 받고, 위젯 기본 스킨(`gou-fab` className, 토큰\n * 아이콘 + APR children)만 버린다. 그래서 지갑 안의 생김새는 딥링크 시절과\n * 1px 도 달라지지 않고, 클릭은 위젯의 액션 선택 모달로 간다.\n */\nexport const OnePopGetTokenTrigger = React.forwardRef<\n  HTMLButtonElement,\n  React.ButtonHTMLAttributes<HTMLButtonElement>\n>(function OnePopGetTokenTrigger(\n  // 위젯 스킨은 의도적으로 버린다.\n  { className: _className, children: _children, ...props },\n  ref,\n) {\n  return (\n    <button\n      {...props}\n      ref={ref}\n      type=\"button\"\n      className=\"wi-op-link-btn\"\n      data-track=\"onepop_get_token\"\n    >\n      <span>Get ONEUSD</span>\n      <OnePopChevronRightIcon size={12} />\n    </button>\n  );\n});\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useWalletInfoContext } from \"../providers/wallet-info-provider\";\n\nexport interface WalletInfoNavProps {\n  position?: \"top\" | \"bottom\";\n  children: React.ReactNode;\n}\n\nexport function WalletInfoNav({\n  position = \"top\",\n  children,\n}: WalletInfoNavProps) {\n  const { setNavContent, setNavPosition } = useWalletInfoContext();\n\n  React.useEffect(() => {\n    setNavContent(children);\n    setNavPosition(position);\n    return () => setNavContent(null);\n  }, [children, position, setNavContent, setNavPosition]);\n\n  return null;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useWalletInfoContext } from \"../providers/wallet-info-provider\";\n\nexport interface WalletInfoFooterProps {\n  children: React.ReactNode;\n}\n\nexport function WalletInfoFooter({ children }: WalletInfoFooterProps) {\n  const { setFooterContent } = useWalletInfoContext();\n\n  React.useEffect(() => {\n    setFooterContent(children);\n    return () => setFooterContent(null);\n  }, [children, setFooterContent]);\n\n  return null;\n}\n","import styleInject from '#style-inject';styleInject(\"[data-theme=dark].wi-popover-content,[data-theme=dark].wi-drawer-content{--wi-primary: var(--ds-accent-primary-high, #f0640d);--wi-secondary: var(--ds-system-red-default, #e70077);--wi-surface-bg: var(--ds-bg-default, #121212);--wi-surface-default: var(--ds-surface-medium, rgba(255, 255, 255, .05));--wi-surface-subtle: var(--ds-surface-high, rgba(255, 255, 255, .1));--wi-border-default: var(--ds-border-default, rgba(255, 255, 255, .05));--wi-border-subtle: var(--ds-border-medium, rgba(255, 255, 255, .1));--wi-texticon-primary: var(--ds-content-highest, #ffffff);--wi-texticon-secondary: var(--ds-content-medium, rgba(255, 255, 255, .7));--wi-texticon-tertiary: var(--ds-content-low, rgba(255, 255, 255, .5));--wi-on-primary: var(--ds-accent-primary-on, #ffffff);--wi-error: var(--ds-system-red-default, #e70077);--wi-shadow: 0 2px 16px -2px rgba(0, 0, 0, .4)}[data-theme=light].wi-popover-content,[data-theme=light].wi-drawer-content{--wi-primary: var(--ds-accent-primary-high, #f0640d);--wi-secondary: var(--ds-system-red-default, #e70077);--wi-surface-bg: var(--ds-bg-default, #ffffff);--wi-surface-default: var(--ds-surface-medium, rgba(18, 18, 18, .05));--wi-surface-subtle: var(--ds-surface-high, rgba(18, 18, 18, .1));--wi-border-default: var(--ds-border-default, rgba(18, 18, 18, .05));--wi-border-subtle: var(--ds-border-medium, rgba(18, 18, 18, .1));--wi-texticon-primary: var(--ds-content-highest, #121212);--wi-texticon-secondary: var(--ds-content-medium, rgba(18, 18, 18, .7));--wi-texticon-tertiary: var(--ds-content-low, rgba(18, 18, 18, .5));--wi-on-primary: var(--ds-accent-primary-on, #ffffff);--wi-error: var(--ds-system-red-default, #e70077);--wi-shadow: 0 2px 16px -2px rgba(0, 0, 0, .15)}\\n\")","import styleInject from '#style-inject';styleInject(\".wi-trigger-btn{display:inline-flex;align-items:center;gap:8px;padding:8px 16px;border:1px solid rgba(255,255,255,.2);background:#1a2332;border-radius:9999px;cursor:pointer;color:#fff;font-family:inherit;font-size:16px;font-weight:500;line-height:24px;letter-spacing:-.3125px;transition:opacity .15s ease}.wi-trigger-btn:hover{opacity:.85}.wi-trigger-btn[data-theme=light]{background:#f3f6f8;border-color:#1212121a;color:#121212}.wi-trigger-btn[data-mobile]{font-size:14px;padding:6px 12px;gap:6px;line-height:20px}.wi-trigger-address{white-space:nowrap}.wi-divider{height:1px;background:var(--wi-border-default);margin:0;border:none;flex-shrink:0}[data-theme=light] .wi-divider{background:#1212121a}[data-theme=dark] .wi-divider{background:#ffffff1a}\\n\")","import styleInject from '#style-inject';styleInject(\"@keyframes wi-fade-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}@keyframes wi-shimmer{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes wi-slide-in-right{0%{transform:translate(40px);opacity:0}to{transform:translate(0);opacity:1}}@keyframes wi-slide-in-left{0%{transform:translate(-40px);opacity:0}to{transform:translate(0);opacity:1}}\\n\")","import styleInject from '#style-inject';styleInject(\".wi-popover-content{-webkit-font-smoothing:antialiased;z-index:100001;width:393px;border-radius:24px;border:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));background:var(--wi-surface-bg);box-shadow:var(--ds-shadow-modal, var(--wi-shadow));animation:wi-fade-in .15s ease-out;height:min(680px,calc(100dvh - 32px));overflow:hidden;display:flex;flex-direction:column}.wi-popover-content[data-no-balance]{height:auto}.wi-popover-content[data-has-footer]:not([data-no-balance]){height:min(calc(680px + var(--wi-footer-height, 0px)),calc(100dvh - 32px))}.wi-drawer-overlay{position:fixed;inset:0;z-index:100000;background:#0009}.wi-drawer-content{-webkit-font-smoothing:antialiased;position:fixed;top:0;right:0;bottom:0;z-index:100001;width:100vw;max-height:100vh;background:var(--wi-surface-bg);overflow:hidden;display:flex;flex-direction:column;box-sizing:border-box}.wi-drawer-content[data-no-balance]{bottom:auto;max-height:none}.wi-drawer-content[data-no-balance][data-direction=bottom]{bottom:0;top:auto;border-radius:24px 24px 0 0}.wi-drawer-content[data-direction=left]{right:auto;left:0}.wi-drawer-content[data-direction=bottom]{top:0;left:0;right:0;width:100%;max-height:100dvh;margin:0 auto}.wi-drawer-content[data-direction=bottom][data-view=qr]{max-height:100dvh}.wi-popover-content[data-view=qr]{max-height:calc(100dvh - 32px)}.wi-sticky-header{flex-shrink:0;position:relative}.wi-header{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:24px}.wi-header--wallet{align-items:flex-start;border-bottom:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wi-header--wallet+.wi-divider{display:none}.wi-header--sub-account{align-items:center}.wi-header--sub-account .wi-header-back{width:32px;height:38px}.wi-header--sub-account .wi-header-action{flex-shrink:0}.wi-account-block{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.wi-account-block--sub-account{align-items:center}.wi-avatar{flex-shrink:0;position:relative;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border-radius:50%;background:var(--wi-surface-bg);box-sizing:border-box;overflow:hidden}.wi-avatar-fallback{position:absolute;inset:0;border-radius:50%;z-index:0}.wi-avatar-img{position:relative;z-index:1;width:32px;height:32px;border-radius:50%;object-fit:cover;background:transparent}.wi-account-text{display:flex;flex-direction:column;gap:2px;min-width:0}.wi-account-row{display:flex;align-items:center;gap:8px;min-width:0}.wi-account-row--btn{padding:2px;margin:0;width:fit-content;border:none;background:transparent;cursor:pointer;color:inherit;font:inherit;border-radius:6px;transition:background-color .15s ease,opacity .15s ease}.wi-account-row--btn:hover{background:var(--wi-surface-subtle, rgba(255, 255, 255, .06))}.wi-account-row--btn:focus-visible{outline:2px solid var(--wi-primary, #f0640d);outline-offset:2px}.wi-account-name{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wi-account-switch{display:inline-flex;align-items:center;justify-content:center;padding:2px 0;border:none;background:transparent;color:var(--ds-content-low, rgba(0, 0, 0, .4));border-radius:4px;flex-shrink:0;transition:color .15s ease,transform .2s ease}.wi-account-switch[data-open=true]{transform:rotate(180deg)}.wi-account-row--btn:hover .wi-account-switch{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-address-row-btn{display:inline-flex;align-items:center;gap:4px;padding:0;border:none;background:transparent;cursor:pointer;color:var(--ds-content-medium, rgba(0, 0, 0, .65));font:inherit;text-align:left;min-width:0;transition:color .15s ease}.wi-address-row-btn:hover .wi-account-address,.wi-address-row-btn:hover .wi-account-copy{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-account-address{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:12px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65));white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wi-account-copy{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;color:var(--wi-texticon-tertiary);flex-shrink:0;transition:color .15s ease}.wi-account-copy[data-copied=true]{color:var(--ds-system-green-high, #00a869)}.wi-header-actions{display:flex;align-items:center;gap:16px;flex-shrink:0;margin-top:-4px}.wi-header-action{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;background:transparent;border-radius:0;cursor:pointer;color:var(--ds-content-low, rgba(0, 0, 0, .4));flex-shrink:0;transition:color .15s ease}.wi-header-action:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-header-action--qr{width:32px;height:32px;border-radius:8px;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-content-medium, rgba(0, 0, 0, .65));transition:background-color .15s ease,color .15s ease}.wi-header-action--qr:hover{background:var(--ds-surface-high, rgba(0, 0, 0, .08));color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-header--receive{align-items:center;border-bottom:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wi-header--receive .wi-header-action,.wi-header--sub-account .wi-header-action{position:absolute;top:24px;right:24px}.wi-receive-title{position:absolute;left:50%;transform:translate(-50%);font-family:inherit;font-size:18px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-highest, #000000);text-align:center;pointer-events:none}.wi-header-info{flex:1;display:flex;flex-direction:column;gap:8px;min-width:0}.wi-header-title{font-family:inherit;font-size:16px;font-weight:600;line-height:1.3;color:var(--wi-texticon-primary);margin:0}.wi-header-close{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border:none;background:var(--wi-surface-default);border-radius:8px;cursor:pointer;color:var(--ds-content-low, #00000066);transition:opacity .15s ease}.wi-header-close:hover{opacity:.7}.wi-total-section{margin:24px 24px 0;padding:0;flex-shrink:0;display:flex;flex-direction:column;gap:8px;border:none;background:transparent;border-radius:12px;color:inherit;font:inherit;text-align:left;width:auto}.wi-action-row{display:flex;align-items:flex-start;gap:8px;width:auto;margin:24px 24px 0}.wi-action-card{appearance:none;display:flex;flex:1 1 0;min-width:0;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:10px 8px;border:none;border-radius:12px;background:var(--ds-surface-medium, #f5f7fa);color:var(--ds-content-high, rgba(0, 0, 0, .85));font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;text-align:center;cursor:pointer;transition:background-color .15s ease,color .15s ease}.wi-action-card:hover:not(:disabled){background:#2e3544!important;color:var(--ds-content-high, rgba(0, 0, 0, .85))}:root[data-ds-theme=light] .wi-action-card:hover:not(:disabled){background:#e4e8ef!important;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-action-card:disabled{cursor:default}.wi-action-card>svg{flex-shrink:0}.wi-action-card:not(:disabled){transition:background-color .15s ease,transform .1s ease}.wi-action-card:active:not(:disabled){transform:scale(.93)}.wi-total-head{display:flex;align-items:center;gap:4px}.wi-total-body{display:flex;align-items:center;justify-content:space-between;gap:8px;min-width:0}.wi-total-value-wrap{position:relative;flex:1 1 auto;min-width:0}.wi-total-send{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;padding:0;flex-shrink:0;border:none;border-radius:9px;background:var(--ds-surface-medium, #f5f7fa);cursor:pointer;transition:background-color .15s ease,transform .1s ease}.wi-total-send:hover:not(:disabled){background:#2e3544}:root[data-ds-theme=light] .wi-total-send:hover:not(:disabled){background:#e4e8ef}.wi-total-send:active:not(:disabled){transform:scale(.93)}.wi-total-send:disabled{cursor:default;opacity:.45}.wi-total-send--dimmed{opacity:.4;cursor:pointer}.wi-total-send--dimmed:hover{opacity:.5}.wi-total-label{font-family:inherit;font-size:14px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65))}.wi-total-eye{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;color:var(--ds-content-low, rgba(0, 0, 0, .4));cursor:pointer;transition:color .15s ease;border-radius:4px}.wi-total-eye:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-total-eye:focus-visible{outline:1px solid var(--wi-texticon-secondary);outline-offset:2px}.wi-total-value{display:flex;align-items:baseline;gap:0;font-family:inherit;font-weight:600;line-height:1.25;color:var(--ds-content-highest, #000000);font-variant-numeric:tabular-nums;min-height:45px;white-space:nowrap}.wi-total-currency,.wi-total-amount,.wi-total-decimal{font-size:calc(36px * var(--wi-total-scale, 1));color:var(--ds-content-highest, #000000)}.wi-total-lte{margin-right:2px;font-weight:500;font-size:calc(22px * var(--wi-total-scale, 1));color:var(--wi-texticon-secondary)}.wi-total-mask{letter-spacing:2px;font-size:calc(32px * var(--wi-total-scale, 1));color:var(--wi-texticon-secondary)}.wi-total-skeleton{border-radius:6px}.wi-scrollable-body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:8px 0 0;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.15) transparent;overscroll-behavior:contain}.wi-drawer-content .wi-scrollable-body{touch-action:pan-y}[data-theme=light] .wi-scrollable-body{scrollbar-color:rgba(0,0,0,.12) transparent}.wi-scrollable-body::-webkit-scrollbar{width:4px}.wi-scrollable-body::-webkit-scrollbar-track{background:transparent}.wi-scrollable-body::-webkit-scrollbar-thumb{background:#ffffff26;border-radius:4px}.wi-scrollable-body::-webkit-scrollbar-thumb:hover{background:#ffffff40}[data-theme=light] .wi-scrollable-body::-webkit-scrollbar-thumb{background:#0000001f}[data-theme=light] .wi-scrollable-body::-webkit-scrollbar-thumb:hover{background:#0003}.wi-nav-fixed{flex-shrink:0;padding:0 24px}.wi-token-list{display:flex;flex-direction:column;gap:0;padding:8px 0}.wi-token-item{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 24px;border:none;background:transparent;text-align:left;font-family:inherit;width:100%;box-sizing:border-box}.wi-token-item--button{cursor:pointer;background:var(--ds-surface-default, #ffffff);transition:none}.wi-token-item--button:hover{background:#252b39!important}:root[data-ds-theme=light] .wi-token-item--button:hover{background:#eff3f8!important}button.wi-token-item:focus-visible{outline:2px solid var(--wi-primary);outline-offset:2px;border-radius:4px}.wi-token-left{display:flex;align-items:center;gap:16px;min-width:0}.wi-token-icon-wrap{position:relative;width:40px;height:40px;flex-shrink:0}.wi-token-icon{width:40px;height:40px;border-radius:0;object-fit:contain;opacity:0;transition:opacity .2s ease}.wi-token-icon[data-loaded=true]{opacity:1}.wi-token-chain-badge{position:absolute;right:0;bottom:0;width:14px;height:14px;border-radius:50%;background:var(--wi-surface-bg);box-shadow:0 0 0 1.5px var(--wi-surface-bg);object-fit:contain}.wi-token-info{display:flex;flex-direction:column;min-width:0}.wi-token-name-row{display:flex;align-items:center;gap:6px;min-width:0}.wi-token-name{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap}.wi-token-apr{flex-shrink:0;font-family:inherit;font-size:12px;font-weight:600;line-height:1;color:var(--ds-system-purple-high, #9b78ff);background:var(--ds-system-purple-low, #2e2854);padding:4px;border-radius:6px;white-space:nowrap}.wi-token-price-row{display:flex;align-items:center;gap:8px}.wi-token-price{font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));white-space:nowrap}.wi-token-change{font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;white-space:nowrap}.wi-token-change[data-positive=true]{color:var(--ds-system-green-high, #00a0a2)}.wi-token-change[data-positive=false]{color:var(--ds-system-red-high, #a80927)}.wi-token-change[data-zero]{color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-token-right{display:flex;flex-direction:column;align-items:flex-end;flex-shrink:0}.wi-token-balance{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-highest, #000000);white-space:nowrap}.wi-token-value{font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));white-space:nowrap}.wi-sticky-footer{flex-shrink:0}.wi-footer{padding:16px 24px 24px}.wi-builtin-footer{display:flex;flex-direction:column;gap:8px;align-items:stretch}.wi-wallet-disconnect-row{display:flex;justify-content:flex-end;flex-shrink:0;padding:16px 24px;border-top:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wi-send-toast{position:absolute;left:15px;right:15px;bottom:20px;z-index:2;display:flex;align-items:center;gap:12px;min-height:52px;padding:12px;border-radius:12px;background:var(--ds-surface-inverted, #1e232e);color:var(--ds-content-inverted, #ffffff);box-sizing:border-box;text-decoration:none}.wi-action-toast{position:absolute;left:15px;right:15px;bottom:20px;z-index:3;padding:12px 14px;border-radius:12px;background:var(--ds-surface-inverted, #1e232e);color:var(--ds-content-inverted, #ffffff);font-size:14px;line-height:1.4;text-align:center;box-sizing:border-box;animation:wi-action-toast-in .18s ease-out}.wi-popover-content:has(.wi-wallet-disconnect-row) .wi-action-toast,.wi-drawer-content:has(.wi-wallet-disconnect-row) .wi-action-toast{bottom:72px}@keyframes wi-action-toast-in{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.wi-action-card--dimmed{opacity:.4;cursor:pointer}.wi-action-card--dimmed:hover{opacity:.5}.wi-popover-content:has(.wi-wallet-disconnect-row) .wi-send-toast,.wi-drawer-content:has(.wi-wallet-disconnect-row) .wi-send-toast{bottom:72px}.wi-send-toast:hover{opacity:.92}.wi-send-toast-icon{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;flex-shrink:0;color:var(--ds-accent-primary-high, #f0640d)}.wi-send-toast-spinner{width:18px;height:18px;border:2px solid rgba(255,159,33,.22);border-top-color:var(--ds-accent-primary-on-inverted, #ff8438);border-radius:50%;animation:wi-send-toast-spin .8s linear infinite}.wi-send-toast-error-dot{width:18px;height:18px;border-radius:50%;background:var(--ds-system-red-default, #ff3358)}@keyframes wi-send-toast-spin{to{transform:rotate(360deg)}}.wi-send-toast-copy{display:flex;min-width:0;flex-direction:column}.wi-send-toast-title{font-family:inherit;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-inverted, #ffffff)}.wi-send-toast-desc{font-family:inherit;font-size:12px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-accent-primary-on-inverted, #ff8438)}.wi-send-toast-icon[data-status=error]+.wi-send-toast-copy .wi-send-toast-desc{color:var(--ds-system-red-default, #ff3358)}.wi-disconnect-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:16px;padding:0;border:none;background:transparent;color:var(--ds-content-low, rgba(0, 0, 0, .4));font-family:inherit;font-size:14px;font-weight:500;line-height:1.5;letter-spacing:-.01em;cursor:pointer;transition:color .15s ease}.wi-disconnect-btn:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-disconnect-label{white-space:nowrap}.wi-legal-links{display:inline-flex;align-items:center;justify-content:center;gap:6px;width:100%;flex-shrink:0;font-family:inherit;font-size:14px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4))}.wi-receive-avatar .wi-avatar-fallback{position:absolute;inset:0;border-radius:50%;z-index:0}.wi-receive-avatar .wi-avatar-img{width:24px;height:24px}.wi-legal-link{color:var(--ds-content-low, rgba(0, 0, 0, .4));text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.wi-legal-link:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-legal-sep{color:inherit}.wi-empty{display:flex;align-items:center;justify-content:center;padding:32px 0;font-family:inherit;font-size:16px;color:var(--wi-texticon-secondary)}.wi-skeleton-item{display:flex;align-items:center;justify-content:space-between;padding:4px 0}.wi-skeleton-left{display:flex;align-items:center;gap:12px}.wi-skeleton-block{border-radius:6px;background:var(--wi-border-default);position:relative;overflow:hidden}.wi-skeleton-block:after{content:\\\"\\\";position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.06) 50%,transparent 100%);animation:wi-shimmer 1.5s ease-in-out infinite}[data-theme=light] .wi-skeleton-block:after{background:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.04) 50%,transparent 100%)}.wi-skeleton-icon{width:32px;height:32px;border-radius:50%;flex-shrink:0}.wi-skeleton-lines{display:flex;flex-direction:column;gap:6px}.wi-skeleton-right{display:flex;flex-direction:column;align-items:flex-end;gap:6px}.wi-header-back{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:none;background:transparent;cursor:pointer;color:var(--ds-content-low, rgba(0, 0, 0, .4));flex-shrink:0;transition:color .15s ease}.wi-header-back:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-view-container{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.wi-popover-content[data-view=qr] .wi-view-container,.wi-drawer-content[data-view=qr] .wi-view-container{flex:1;min-height:0}.wi-view-inner{display:flex;flex-direction:column;flex:1;min-height:0}.wi-view-wallet{animation:wi-slide-in-left .25s ease-out}.wi-view-qr,.wi-view-portfolio{animation:wi-slide-in-right .25s ease-out}.wi-qr-body{flex:1;min-height:0;display:flex;flex-direction:column}.wi-portfolio-body{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.wi-portfolio-body>.wp-embed{flex:1;min-height:0}.wi-portfolio-body .wp-body-inner{padding:16px 16px 32px;gap:32px}.wi-receive-section{flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:24px;padding:24px}.wi-receive-account{display:inline-flex;align-items:center;gap:8px;padding:4px 8px 4px 4px;border:0;background:var(--ds-surface-high, #edf1f2);border-radius:9999px;font-family:inherit;color:inherit}.wi-receive-account--btn{cursor:pointer;transition:none}.wi-receive-account--btn:hover{background:#2e3544}:root[data-ds-theme=light] .wi-receive-account--btn:hover{background:#e4e8ef}.wi-receive-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:50%;overflow:hidden;flex-shrink:0}.wi-receive-account-name{font-family:inherit;font-size:16px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap}.wi-receive-account>svg{color:var(--ds-content-medium, rgba(0, 0, 0, .65));flex-shrink:0}.wi-receive-chevron{display:inline-flex;align-items:center;color:var(--ds-content-medium, rgba(0, 0, 0, .65));flex-shrink:0;transition:transform .2s ease}.wi-receive-chevron[data-open=true]{transform:rotate(180deg)}.wi-qr-block{display:flex;flex-direction:column;align-items:center;gap:16px}.wi-qr-box{position:relative;display:flex;align-items:center;justify-content:center;padding:10px;background:#fff;border-radius:6px;overflow:hidden}.wi-qr-logo{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);pointer-events:none}.wi-qr-address{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:16px;font-weight:400;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65));text-align:center;word-break:break-all;max-width:281px;margin:0}.wi-qr-copy-btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;padding:12px 16px;border:1px solid var(--ds-border-default, rgba(0, 0, 0, .07));background:var(--ds-surface-default, #ffffff);border-radius:12px;cursor:pointer;transition:border-color .15s ease}.wi-qr-copy-btn:hover{border-color:var(--ds-border-medium, rgba(0, 0, 0, .18))}.wi-qr-copy-icon{display:inline-flex;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wi-qr-copy-text{font-family:inherit;font-size:16px;font-weight:500;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap}.wi-receive-footer{flex-shrink:0;display:flex;align-items:center;justify-content:center;padding:16px 24px;border-top:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wi-drawer-content .wi-header{padding:16px}.wi-drawer-content .wi-header--receive .wi-header-action,.wi-drawer-content .wi-header--sub-account .wi-header-action{top:16px;right:16px}.wi-drawer-content .wi-total-section,.wi-drawer-content .wi-action-row{margin:16px 16px 0}.wi-drawer-content .wi-nav-fixed{padding:0 16px}.wi-drawer-content .wi-token-item{padding:12px 16px}.wi-drawer-content .wi-wallet-disconnect-row{padding:16px}\\n\")","import styleInject from '#style-inject';styleInject(\".wi-td-header-title{position:absolute;left:50%;transform:translate(-50%);display:inline-flex;align-items:center;gap:8px;pointer-events:none}.wi-td-header-icon{width:24px;height:24px;object-fit:contain;flex-shrink:0}.wi-td-header-name{font-family:inherit;font-size:18px;font-weight:600;line-height:1.5;letter-spacing:-.01em;color:var(--wi-texticon-primary, #ffffff)}.wi-td-scroll{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;display:flex;flex-direction:column;gap:24px;padding:20px 24px 24px;background:transparent;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.15) transparent}[data-theme=light] .wi-td-scroll{scrollbar-color:rgba(0,0,0,.12) transparent}.wi-td-scroll::-webkit-scrollbar{width:4px}.wi-td-scroll::-webkit-scrollbar-track{background:transparent}.wi-td-scroll::-webkit-scrollbar-thumb{background:#ffffff26;border-radius:4px}.wi-td-scroll::-webkit-scrollbar-thumb:hover{background:#ffffff40}[data-theme=light] .wi-td-scroll::-webkit-scrollbar-thumb{background:#0000001f}[data-theme=light] .wi-td-scroll::-webkit-scrollbar-thumb:hover{background:#0003}.wi-td-chart{display:flex;flex-direction:column;gap:14px}.wi-td-chart-panel{display:flex;flex-direction:column;gap:4px}.wi-td-chart-price{font-size:26px;font-weight:700;line-height:1.15;color:var(--wi-texticon-primary, #ffffff)}.wi-td-chart-change{display:inline-flex;align-items:center;gap:8px;font-size:14px;font-weight:600;color:var(--ds-system-green-high, #00a869)}.wi-td-chart-change[data-positive=false]{color:var(--wi-error, #e70077)}.wi-td-chart-when{font-weight:400;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5))}.wi-td-chart-plot{position:relative;width:100%;height:150px}.wi-td-chart-svg{display:block;width:100%;height:150px;overflow:visible;touch-action:none}.wi-td-chart-minmax{fill:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5));font-size:11px;font-family:inherit}.wi-td-chart-cursor{stroke:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5));stroke-width:1;stroke-dasharray:3 3}.wi-td-chart-skeleton{width:100%;height:150px;border-radius:12px}.wi-td-chart-tabs{display:flex;gap:6px;justify-content:space-between}.wi-td-chart-tab{flex:1;padding:7px 0;border:none;border-radius:8px;background:transparent;cursor:pointer;font-family:inherit;font-size:13px;font-weight:600;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5));transition:background .15s ease,color .15s ease}.wi-td-chart-tab[data-active]{background:var(--wi-surface-subtle, rgba(255, 255, 255, .1));color:var(--wi-texticon-primary, #ffffff)}.wi-td-chart-tab:hover:not([data-active]){color:var(--wi-texticon-primary, #ffffff)}.wi-td-balance-card{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:20px;border-radius:16px;background:var(--wi-surface-default, rgba(255, 255, 255, .05))}.wi-td-balance-left{display:flex;flex-direction:column;gap:4px;flex:1 1 0;min-width:0}.wi-td-balance-label{font-size:15px;font-weight:600;color:var(--wi-texticon-primary, #ffffff)}.wi-td-balance-amount{font-size:30px;font-weight:700;line-height:1.15;color:var(--wi-texticon-primary, #ffffff);white-space:nowrap;max-width:100%}.wi-td-balance-fiat{font-size:14px;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5))}.wi-td-send-btn{display:inline-flex;align-items:center;gap:6px;flex-shrink:0;padding:12px 22px;border:none;border-radius:999px;cursor:pointer;font-family:inherit;font-size:15px;font-weight:600;color:var(--wi-on-primary, #ffffff);background:var(--wi-primary, #f0640d);transition:filter .15s ease}.wi-td-send-btn:hover{filter:brightness(1.06)}.wi-td-send-btn:active{filter:brightness(.94)}.wi-td-section{display:flex;flex-direction:column;gap:14px}.wi-td-section-title{margin:0;font-size:18px;font-weight:700;letter-spacing:-.01em;color:var(--wi-texticon-primary, #ffffff)}.wi-td-rows{display:flex;flex-direction:column;gap:12px}.wi-td-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.wi-td-row-label{font-size:15px;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5))}.wi-td-row-value{font-size:15px;font-weight:500;color:var(--wi-texticon-primary, #ffffff);text-align:right;word-break:break-all}.wi-td-row-link,.wi-td-row-copy{display:inline-flex;align-items:center;gap:6px;border:none;background:transparent;padding:0;cursor:pointer;font-family:inherit;text-decoration:none}.wi-td-row-link:hover,.wi-td-row-copy:hover{opacity:.8}.wi-td-external-icon{color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5));flex-shrink:0}.wi-td-row-copy svg{color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5));flex-shrink:0}.wi-td-social-row{display:flex;flex-wrap:wrap;gap:10px}.wi-td-social-btn{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;color:var(--wi-texticon-primary, #ffffff);background:var(--wi-surface-default, rgba(255, 255, 255, .05));text-decoration:none;transition:background .15s ease}.wi-td-social-btn:hover{background:var(--wi-surface-subtle, rgba(255, 255, 255, .1))}.wi-td-history-list{display:flex;flex-direction:column;gap:10px}.wi-td-history-item{display:flex;flex-direction:column;gap:8px;padding:16px;border-radius:12px;background:var(--wi-surface-default, rgba(255, 255, 255, .05));text-decoration:none}.wi-td-history-item--link{cursor:pointer;transition:background .15s ease}.wi-td-history-item--link:hover{background:var(--wi-surface-subtle, rgba(255, 255, 255, .1))}.wi-td-history-top,.wi-td-history-bottom{display:flex;align-items:center;justify-content:space-between;gap:10px}.wi-td-history-type{font-size:15px;font-weight:600;color:var(--wi-texticon-primary, #ffffff)}.wi-td-history-type[data-direction=receive],.wi-td-history-type[data-direction=bridge-in]{color:var(--ds-system-green-high, #00a869)}.wi-td-history-counter{font-size:14px;color:var(--wi-primary, #f0640d);font-variant-numeric:tabular-nums}.wi-td-history-meta{display:inline-flex;align-items:center;gap:8px;min-width:0}.wi-td-history-status{font-size:14px;font-weight:500}.wi-td-history-status[data-status=success]{color:var(--ds-system-green-high, #00a869)}.wi-td-history-status[data-status=fail]{color:var(--wi-error, #e70077)}.wi-td-history-time{font-size:14px;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5))}.wi-td-history-amount{font-size:15px;font-weight:600;color:var(--wi-texticon-primary, #ffffff);text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}.wi-td-history-amount[data-size=sm]{font-size:13px}.wi-td-history-amount[data-size=xs]{font-size:11px;white-space:normal;word-break:break-all}.wi-td-history-skeleton{display:flex;align-items:center;justify-content:space-between;padding:16px;border-radius:12px;background:var(--wi-surface-default, rgba(255, 255, 255, .05))}.wi-td-history-empty{display:flex;flex-direction:column;align-items:center;gap:10px;padding:28px 16px;border-radius:12px;background:var(--wi-surface-default, rgba(255, 255, 255, .05));color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5));font-size:14px;text-align:center}.wi-td-retry-btn{padding:8px 18px;border:1px solid var(--wi-border-subtle, rgba(255, 255, 255, .1));border-radius:999px;background:transparent;color:var(--wi-texticon-primary, #ffffff);font-family:inherit;font-size:13px;font-weight:600;cursor:pointer}.wi-td-retry-btn:hover{background:var(--wi-surface-subtle, rgba(255, 255, 255, .1))}.wi-td-history-sentinel{display:flex;align-items:center;justify-content:center;min-height:32px}.wi-td-history-loading{font-size:13px;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5))}.wi-td-loadmore-btn{align-self:center;margin-top:2px;padding:10px 24px;border:1px solid var(--wi-border-subtle, rgba(255, 255, 255, .1));border-radius:999px;background:transparent;color:var(--wi-texticon-primary, #ffffff);font-family:inherit;font-size:14px;font-weight:600;cursor:pointer;transition:background .15s ease}.wi-td-loadmore-btn:hover:not(:disabled){background:var(--wi-surface-subtle, rgba(255, 255, 255, .1))}.wi-td-loadmore-btn:disabled{cursor:default;color:var(--wi-texticon-tertiary, rgba(255, 255, 255, .5))}\\n\")","import styleInject from '#style-inject';styleInject(\".wi-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}\\n\")","\"use client\";\n\nimport * as React from \"react\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport {\n  type AppsConfig,\n  useRemoteConfigValue,\n} from \"../../../lib/remote-config\";\nimport type { GlobalMenu } from \"../../app-launcher/types\";\nimport { TrackingBoundary } from \"../../../lib/sentry\";\nimport {\n  BridgeFlow,\n  type BridgeGetToTokensFn,\n  type BridgeSubmittedInfo,\n} from \"../../bridge\";\nimport { RelayDeposit } from \"../../relay\";\nimport { ONEUSD_LOGO } from \"../../relay/lib/token-logos\";\nimport {\n  ResponsiveShell,\n  ShellContent,\n} from \"../../relay/components/shell/ResponsiveShell\";\nimport {\n  isGetOneUsdTargetAvailable,\n  resolveGetOneUsdRoute,\n} from \"../lib/token-refs\";\nimport {\n  isGetOneUsdActionSupported,\n  isGetOneUsdActionVisible,\n  isGetOneUsdActionEnabled,\n  type GetOneUsdRemoteMinVersions,\n  type GetOneUsdRemoteModes,\n} from \"../lib/action-modes\";\nimport {\n  isOneUsdHostname,\n  ONE_USD_SITE_URLS,\n  resolveOneUsdSiteUrl,\n} from \"../lib/one-usd-site\";\nimport { useGetOneUsdApr } from \"../hooks/use-get-one-usd-apr\";\nimport { formatCrossdAprPercent } from \"../../wallet-info/queries/use-crossd-apr\";\nimport { InfoCircleIcon } from \"../../wallet-portfolio/components/icons\";\nimport {\n  getTransactionPendingLabel,\n  getTransactionSuccessNote,\n} from \"../lib/transaction-status\";\nimport type {\n  GetOneUsdActionId,\n  GetOneUsdEvent,\n  GetOneUsdProps,\n} from \"../types\";\nimport {\n  BridgeActionIcon,\n  ChevronRightActionIcon,\n  CloseActionIcon,\n  SwapActionIcon,\n  TransferActionIcon,\n} from \"./icons\";\nimport \"../styles/component.css\";\n\nconst ACTIONS: Array<{\n  id: GetOneUsdActionId;\n  label: string;\n  eta?: string;\n  Icon: typeof SwapActionIcon;\n}> = [\n  { id: \"swap\", label: \"Swap\", eta: \"Instant\", Icon: SwapActionIcon },\n  { id: \"bridge\", label: \"Bridge\", Icon: BridgeActionIcon },\n  {\n    id: \"transfer\",\n    label: \"Transfer Crypto\",\n    eta: \"QR deposit\",\n    Icon: TransferActionIcon,\n  },\n];\n\ntype TransactionStatusPhase = \"pending\" | \"success\" | \"failure\";\n\ninterface TransactionStatus {\n  id: number;\n  phase: TransactionStatusPhase;\n  action: \"swap\" | \"bridge\";\n  label: string;\n  txHash: string;\n}\n\nconst DEFAULT_TRANSACTION_RESULT_DURATION_MS = 4_000;\n\nfunction errorMessage(error: unknown): string {\n  if (error instanceof Error) return error.message;\n  return typeof error === \"string\" ? error : \"Unknown error\";\n}\n\nfunction callSafely<T>(callback: ((value: T) => void) | undefined, value: T) {\n  try {\n    callback?.(value);\n  } catch {\n    // Consumer callbacks must not interrupt transaction state tracking.\n  }\n}\n\nfunction useCollapsedTrigger({\n  compactTrigger,\n  collapseOnScroll,\n  scrollThreshold,\n}: Pick<\n  GetOneUsdProps,\n  \"compactTrigger\" | \"collapseOnScroll\" | \"scrollThreshold\"\n>): boolean {\n  const [scrolled, setScrolled] = React.useState(false);\n\n  React.useEffect(() => {\n    if (collapseOnScroll === false) {\n      setScrolled(false);\n      return;\n    }\n    const threshold = scrollThreshold ?? 200;\n    // 접힘/펼침 경계에 24px 히스테리시스를 둔다. 임계값 하나만 쓰면 인앱 웹뷰의\n    // 관성/바운스 스크롤이 경계에서 미세하게 튈 때 상태가 깜빡인다.\n    const collapseAt = threshold + 24;\n    const expandAt = Math.max(0, threshold - 24);\n    let frame = 0;\n    let collapsed = window.scrollY > threshold;\n    setScrolled(collapsed);\n\n    const update = () => {\n      frame = 0;\n      const next = collapsed ? window.scrollY > expandAt : window.scrollY > collapseAt;\n      if (next === collapsed) return;\n      collapsed = next;\n      setScrolled(next);\n    };\n    // 스크롤 이벤트마다 setState 하면 웹뷰에서 프레임이 밀린다. rAF로 프레임당 1회.\n    const onScroll = () => {\n      if (frame) return;\n      frame = requestAnimationFrame(update);\n    };\n\n    window.addEventListener(\"scroll\", onScroll, { passive: true });\n    return () => {\n      if (frame) cancelAnimationFrame(frame);\n      window.removeEventListener(\"scroll\", onScroll);\n    };\n  }, [collapseOnScroll, scrollThreshold]);\n\n  return Boolean(compactTrigger || scrolled);\n}\n\nfunction RouteUnavailable({ loading }: { loading: boolean }) {\n  return (\n    <div className=\"gou-route-state\" role=\"status\">\n      {loading && <span className=\"gou-spinner\" aria-hidden=\"true\" />}\n      <h3>{loading ? \"Preparing route\" : \"Route unavailable\"}</h3>\n      <p>\n        {loading\n          ? \"Loading token and contract configuration.\"\n          : \"The configured ONEUSD route is not available in this environment.\"}\n      </p>\n    </div>\n  );\n}\n\nexport function GetOneUsd({\n  walletAddress,\n  tokens,\n  pairs,\n  loading = false,\n  bridge,\n  relay,\n  env,\n  targetSymbol: targetSymbolProp,\n  relayTargetSymbol = \"ONEUSD\",\n  targetIconUrl,\n  mode = \"floating\",\n  buttonComponent,\n  theme = \"dark\",\n  mobileBreakpoint = 768,\n  drawerDirection = \"bottom\",\n  dialogWidth = \"480px\",\n  drawerMaxWidth,\n  drawerMinWidth,\n  style,\n  className,\n  contentClassName,\n  hidden = false,\n  compactTrigger,\n  collapseOnScroll = true,\n  scrollThreshold = 200,\n  waitForTransaction,\n  transactionResultDurationMs = DEFAULT_TRANSACTION_RESULT_DURATION_MS,\n  onRequestConnect,\n  onEvent,\n}: GetOneUsdProps) {\n  const [chooserOpen, setChooserOpen] = React.useState(false);\n  const [activeAction, setActiveAction] =\n    React.useState<GetOneUsdActionId | null>(null);\n  const [relayMounted, setRelayMounted] = React.useState(false);\n  const [transactionStatus, setTransactionStatus] =\n    React.useState<TransactionStatus | null>(null);\n  const [showLearnMore, setShowLearnMore] = React.useState(false);\n  const transactionIdRef = React.useRef(0);\n  const transactionLockedRef = React.useRef(false);\n  const resultTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n  const mountedRef = React.useRef(true);\n  const iconOnly = useCollapsedTrigger({\n    compactTrigger,\n    collapseOnScroll,\n    scrollThreshold,\n  });\n  const resolvedEnv = React.useMemo(() => resolveEnvironment(env), [env]);\n  const remoteModes = useRemoteConfigValue<\n    AppsConfig,\n    GetOneUsdRemoteModes\n  >(\"apps.json\", resolvedEnv, (config) => config?.getOneUsd?.modes);\n  const remoteMinVersions = useRemoteConfigValue<\n    AppsConfig,\n    GetOneUsdRemoteMinVersions\n  >(\"apps.json\", resolvedEnv, (config) => config?.getOneUsd?.minVersions);\n  const remoteOneUsdSiteUrl = useRemoteConfigValue<GlobalMenu, string>(\n    \"app-launcher-v2.json\",\n    resolvedEnv,\n    (config) => resolveOneUsdSiteUrl(config, resolvedEnv),\n  );\n  const oneUsdSiteUrl = remoteOneUsdSiteUrl ?? ONE_USD_SITE_URLS[resolvedEnv];\n  const availableActions = React.useMemo(\n    () => ACTIONS.filter(({ id }) => isGetOneUsdActionVisible(id, remoteModes)),\n    [remoteModes],\n  );\n  const hasEnabledActions = availableActions.length > 0;\n  const aprPercent = useGetOneUsdApr(!hidden && hasEnabledActions);\n\n  const swapRoute = React.useMemo(\n    () => resolveGetOneUsdRoute(tokens, pairs, \"swap\"),\n    [pairs, tokens],\n  );\n  const bridgeRoute = React.useMemo(\n    () => resolveGetOneUsdRoute(tokens, pairs, \"bridge\"),\n    [pairs, tokens],\n  );\n  const targetToken = swapRoute.targetToken ?? bridgeRoute.targetToken;\n  const targetSymbol = targetSymbolProp ?? targetToken?.symbol ?? \"ONEUSD\";\n  const iconUrl = targetIconUrl ?? targetToken?.iconUrl ?? ONEUSD_LOGO;\n  const triggerIconOnly = mode === \"floating\" && iconOnly;\n  const TriggerComponent =\n    mode === \"button\" && buttonComponent ? buttonComponent : \"button\";\n\n  React.useEffect(() => {\n    mountedRef.current = true;\n    return () => {\n      mountedRef.current = false;\n      if (resultTimerRef.current) clearTimeout(resultTimerRef.current);\n    };\n  }, []);\n\n  React.useEffect(() => {\n    setShowLearnMore(!isOneUsdHostname(window.location.hostname));\n  }, []);\n\n  React.useEffect(() => {\n    if (!hasEnabledActions) setChooserOpen(false);\n  }, [hasEnabledActions]);\n\n  const emit = React.useCallback(\n    (event: GetOneUsdEvent) => onEvent?.(event),\n    [onEvent],\n  );\n\n  const openChooser = React.useCallback(() => {\n    if (transactionStatus || !hasEnabledActions) return;\n    emit({\n      name: \"fab_click\",\n      state: triggerIconOnly ? \"icon_only\" : \"expanded\",\n    });\n    setChooserOpen(true);\n  }, [emit, hasEnabledActions, transactionStatus, triggerIconOnly]);\n\n  const selectAction = React.useCallback(\n    (action: GetOneUsdActionId) => {\n      if (\n        transactionStatus ||\n        !isGetOneUsdActionEnabled(action, remoteModes, remoteMinVersions)\n      ) {\n        return;\n      }\n      emit({ name: \"action_select\", action });\n      setChooserOpen(false);\n      if (action === \"transfer\") setRelayMounted(true);\n      setActiveAction(action);\n    },\n    [emit, remoteModes, remoteMinVersions, transactionStatus],\n  );\n\n  const requestConnect = React.useCallback(\n    (action: GetOneUsdActionId) => {\n      emit({ name: \"connect_click\", action });\n      void Promise.resolve(onRequestConnect?.()).catch((error) => {\n        emit({ name: \"failure\", action, message: errorMessage(error) });\n      });\n    },\n    [emit, onRequestConnect],\n  );\n\n  const bridgeAction =\n    activeAction === \"swap\" || activeAction === \"bridge\"\n      ? activeAction\n      : null;\n  const route = bridgeAction === \"swap\" ? swapRoute : bridgeRoute;\n  const routeReady = Boolean(\n    pairs && route.targetToken && route.fromTokens.length > 0,\n  );\n\n  const getToTokens = React.useMemo<BridgeGetToTokensFn | undefined>(() => {\n    if (!route.targetToken) return undefined;\n    const target = route.targetToken;\n    return (fromToken) => {\n      const available = bridge.getToTokens?.(fromToken, tokens);\n      if (available && !isGetOneUsdTargetAvailable(available, target)) return [];\n      return [target];\n    };\n  }, [bridge.getToTokens, route.targetToken, tokens]);\n\n  const {\n    onSubmitted: onBridgeSubmitted,\n    onSuccess: onBridgeSuccess,\n    onFailed: onBridgeFailed,\n    ...bridgeProps\n  } = bridge;\n  const {\n    onDeposited: onRelayDeposited,\n    onError: onRelayError,\n    ...relayProps\n  } = relay ?? {};\n\n  const showTransactionResult = React.useCallback(\n    (\n      id: number,\n      phase: \"success\" | \"failure\",\n      action: \"swap\" | \"bridge\",\n      label: string,\n      txHash: string,\n    ) => {\n      if (!mountedRef.current || transactionIdRef.current !== id) return;\n      setTransactionStatus({ id, phase, action, label, txHash });\n      if (resultTimerRef.current) clearTimeout(resultTimerRef.current);\n      resultTimerRef.current = setTimeout(() => {\n        if (!mountedRef.current || transactionIdRef.current !== id) return;\n        transactionLockedRef.current = false;\n        setTransactionStatus(null);\n        resultTimerRef.current = null;\n      }, Math.max(0, transactionResultDurationMs));\n    },\n    [transactionResultDurationMs],\n  );\n\n  const handleBridgeSubmitted = React.useCallback(\n    (info: BridgeSubmittedInfo, action: \"swap\" | \"bridge\") => {\n      if (transactionLockedRef.current) return;\n      transactionLockedRef.current = true;\n      const id = ++transactionIdRef.current;\n      const pendingLabel = getTransactionPendingLabel(info, action);\n      setTransactionStatus({\n        id,\n        phase: \"pending\",\n        action,\n        label: pendingLabel,\n        txHash: info.txHash,\n      });\n      setChooserOpen(false);\n      setActiveAction(null);\n      callSafely(onBridgeSubmitted, info);\n\n      void Promise.resolve()\n        .then(() => waitForTransaction?.(info))\n        .then(() => {\n          if (!mountedRef.current || transactionIdRef.current !== id) return;\n          showTransactionResult(\n            id,\n            \"success\",\n            action,\n            getTransactionSuccessNote(info, action),\n            info.txHash,\n          );\n          emit({ name: \"success\", action, txHash: info.txHash });\n        })\n        .catch((error) => {\n          if (!mountedRef.current || transactionIdRef.current !== id) return;\n          const message = errorMessage(error);\n          showTransactionResult(\n            id,\n            \"failure\",\n            action,\n            \"Transaction failed\",\n            info.txHash,\n          );\n          callSafely(onBridgeFailed, { message, txHash: info.txHash });\n          emit({ name: \"failure\", action, message });\n        });\n    },\n    [\n      emit,\n      onBridgeFailed,\n      onBridgeSubmitted,\n      showTransactionResult,\n      waitForTransaction,\n    ],\n  );\n\n  return (\n    <TrackingBoundary feature=\"get_one_usd\">\n      <div className={className} data-theme={theme} data-gou-mode={mode}>\n        <ResponsiveShell\n          theme={theme}\n          mobileBreakpoint={mobileBreakpoint}\n          drawerDirection={drawerDirection}\n          dialogWidth={dialogWidth}\n          drawerMaxWidth={drawerMaxWidth}\n          drawerMinWidth={drawerMinWidth}\n          open={chooserOpen}\n          onOpenChange={setChooserOpen}\n        >\n          {!hidden &&\n            hasEnabledActions &&\n            (mode === \"button\" || !transactionStatus) && (\n            <TriggerComponent\n              type=\"button\"\n              className=\"gou-fab\"\n              style={style}\n              data-trigger-mode={mode}\n              data-icon-only={triggerIconOnly ? \"true\" : \"false\"}\n              onClick={openChooser}\n              disabled={Boolean(transactionStatus)}\n              aria-label={`Get ${targetSymbol}`}\n              data-track=\"fab_click\"\n            >\n              {mode === \"floating\" && (\n                <span className=\"gou-tooltip\">Choose how to get {targetSymbol}</span>\n              )}\n              <span className=\"gou-fab-icon-frame\">\n                <img className=\"gou-fab-icon\" src={iconUrl} alt=\"\" />\n              </span>\n              <span className=\"gou-fab-label gou-fab-trigger-copy\">\n                <span>Get {targetSymbol}</span>\n                {aprPercent !== null && (\n                  <span className=\"gou-fab-apr\">\n                    {formatCrossdAprPercent(aprPercent)}\n                  </span>\n                )}\n              </span>\n            </TriggerComponent>\n          )}\n          {transactionStatus && (\n            <div\n              className=\"gou-fab gou-fab-status\"\n              style={mode === \"floating\" ? style : undefined}\n              data-floating-status=\"true\"\n              data-icon-only=\"false\"\n              data-tx-phase={transactionStatus.phase}\n              role=\"status\"\n              aria-live=\"polite\"\n              aria-label={\n                transactionStatus.phase === \"pending\"\n                  ? `${transactionStatus.label} transaction in progress`\n                  : transactionStatus.label\n              }\n            >\n              <span className=\"gou-fab-icon-frame\">\n                <img className=\"gou-fab-icon\" src={iconUrl} alt=\"\" />\n                {transactionStatus.phase === \"pending\" && (\n                  <span className=\"gou-fab-progress-ring\" aria-hidden=\"true\" />\n                )}\n              </span>\n              <span className=\"gou-fab-label\">\n                {transactionStatus.phase === \"pending\" ? (\n                  <>\n                    {transactionStatus.label}\n                    <span className=\"gou-fab-dots\" aria-hidden=\"true\">\n                      <span>.</span><span>.</span><span>.</span>\n                    </span>\n                  </>\n                ) : (\n                  transactionStatus.label\n                )}\n              </span>\n            </div>\n          )}\n          <ShellContent\n            ariaTitle={`Get ${targetSymbol}`}\n            className={`gou-shell gou-chooser ${contentClassName ?? \"\"}`}\n          >\n            <div className=\"gou-chooser-body\">\n              <button\n                type=\"button\"\n                className=\"gou-close\"\n                onClick={() => setChooserOpen(false)}\n                aria-label=\"Close\"\n              >\n                <CloseActionIcon />\n              </button>\n              <header className=\"gou-intro\">\n                <h2>Get {targetSymbol}</h2>\n                {showLearnMore && (\n                  <a\n                    className=\"gou-intro-link\"\n                    href={oneUsdSiteUrl}\n                    target=\"_blank\"\n                    rel=\"noopener noreferrer\"\n                    aria-label=\"What is ONEUSD? Opens in a new tab\"\n                    data-track=\"oneusd_learn_more\"\n                  >\n                    <InfoCircleIcon size={16} className=\"gou-intro-link-icon\" />\n                    <span>What is ONEUSD?</span>\n                  </a>\n                )}\n              </header>\n              <ul className=\"gou-actions\">\n                {availableActions.map(({ id, label, eta, Icon }) => {\n                  const description =\n                    id === \"swap\"\n                      ? `Swap the ${swapRoute.fromTokens[0]?.symbol ?? \"ONE\"} you already hold`\n                      : id === \"bridge\"\n                        ? `Bridge USDT to ${targetSymbol}`\n                        : \"Deposit from any chain or exchange\";\n                  const feeDelegated =\n                    id === \"swap\"\n                      ? pairs?.swap.feeDelegated\n                      : id === \"bridge\"\n                        ? pairs?.bridge.feeDelegated\n                        : false;\n                  // 버전 미달은 숨기지 않고 비활성 — 사라지면 기능이 없어진\n                  // 줄 알지만, 비활성 + 안내면 업데이트하면 된다는 게 전달된다.\n                  const supported = isGetOneUsdActionSupported(\n                    id,\n                    remoteMinVersions,\n                  );\n                  return (\n                    <li key={id}>\n                      <button\n                        type=\"button\"\n                        className=\"gou-action\"\n                        onClick={() => selectAction(id)}\n                        disabled={!supported}\n                        data-supported={supported ? \"true\" : \"false\"}\n                        data-track=\"action_select\"\n                        data-track-id={id}\n                      >\n                        <span className=\"gou-action-icon\"><Icon /></span>\n                        <span className=\"gou-action-copy\">\n                          <span className=\"gou-action-title\">\n                            <span>{label}</span>\n                            {feeDelegated && supported && (\n                              <span className=\"gou-badge\">Fee Delegated</span>\n                            )}\n                            {!supported && (\n                              <span className=\"gou-badge\" data-tone=\"muted\">\n                                Update required\n                              </span>\n                            )}\n                          </span>\n                          {/* eta는 우측 별도 열이 아니라 설명문 뒤로 붙인다 — 오른쪽\n                              끝은 chevron 하나만 두고, 잘리던 설명문도 살린다. */}\n                          <span className=\"gou-action-description\">\n                            {!supported\n                              ? \"Update the app to use this option\"\n                              : eta\n                                ? `${description} · ${eta}`\n                                : description}\n                          </span>\n                        </span>\n                        <ChevronRightActionIcon className=\"gou-action-chevron\" />\n                      </button>\n                    </li>\n                  );\n                })}\n              </ul>\n            </div>\n          </ShellContent>\n        </ResponsiveShell>\n\n        <ResponsiveShell\n          theme={theme}\n          mobileBreakpoint={mobileBreakpoint}\n          drawerDirection={drawerDirection}\n          dialogWidth={dialogWidth}\n          drawerMaxWidth={drawerMaxWidth}\n          drawerMinWidth={drawerMinWidth}\n          open={Boolean(bridgeAction)}\n          onOpenChange={(open) => {\n            if (!open) setActiveAction(null);\n          }}\n        >\n          <ShellContent\n            ariaTitle={`${bridgeAction === \"bridge\" ? \"Bridge\" : \"Swap\"} to ${targetSymbol}`}\n            className={`gou-shell gou-execution ${contentClassName ?? \"\"}`}\n          >\n            {!routeReady ? (\n              <RouteUnavailable loading={loading || !pairs} />\n            ) : (\n              <BridgeFlow\n                key={`${bridgeAction}:${route.fromTokens.map((token) => `${token.chainId}:${token.address}`).join(\",\")}`}\n                {...bridgeProps}\n                env={env}\n                variant=\"embedded\"\n                title={bridgeAction === \"bridge\" ? \"Bridge\" : \"Swap\"}\n                // 태그가 붙는 곳은 라우트의 swap 구간이므로 액션이 Bridge 여도\n                // swap 페어의 대납 여부를 본다 — bridge-swap 경로의 마지막\n                // 스왑도 같은 ONEUSD 페어를 탄다. 순수 브릿지 경로에는 swap\n                // 구간이 없어 태그도 자연히 안 붙는다.\n                feeDelegated={pairs?.swap.feeDelegated}\n                walletAddress={walletAddress ?? \"\"}\n                isConnected={Boolean(walletAddress)}\n                onRequestConnect={() => requestConnect(bridgeAction ?? \"swap\")}\n                tokens={route.fromTokens}\n                initialFromToken={route.fromTokens[0]}\n                initialToToken={route.targetToken}\n                getToTokens={getToTokens}\n                onClose={() => setActiveAction(null)}\n                // 헤더 뒤로가기 → 액션 선택 화면(chooser)으로 복귀. 닫기(X)는\n                // 그대로 위젯 전체를 닫는다.\n                onBackToWallet={() => {\n                  setActiveAction(null);\n                  setChooserOpen(true);\n                }}\n                onSubmitted={(info) => {\n                  handleBridgeSubmitted(info, bridgeAction ?? \"swap\");\n                }}\n                onSuccess={(info) => {\n                  callSafely(onBridgeSuccess, info);\n                }}\n                onFailed={(info) => {\n                  callSafely(onBridgeFailed, info);\n                  emit({\n                    name: \"failure\",\n                    action: bridgeAction ?? \"swap\",\n                    message: info.message,\n                  });\n                }}\n              />\n            )}\n          </ShellContent>\n        </ResponsiveShell>\n\n        {relayMounted && (\n          <RelayDeposit\n            {...relayProps}\n            environment={relayProps.environment ?? env}\n            theme={relayProps.theme ?? theme}\n            mobileBreakpoint={relayProps.mobileBreakpoint ?? mobileBreakpoint}\n            drawerDirection={relayProps.drawerDirection ?? drawerDirection}\n            dialogWidth={relayProps.dialogWidth ?? dialogWidth}\n            drawerMaxWidth={relayProps.drawerMaxWidth ?? drawerMaxWidth}\n            drawerMinWidth={relayProps.drawerMinWidth ?? drawerMinWidth}\n            walletAddress={walletAddress}\n            open={activeAction === \"transfer\"}\n            onOpenChange={(open) => {\n              if (!open) setActiveAction(null);\n            }}\n            requireWalletConnection\n            showRecovery={relayProps.showRecovery ?? true}\n            showRecipientInput={false}\n            hideAmount\n            targetTokens={[relayTargetSymbol]}\n            defaultTarget={relayTargetSymbol}\n            onRequestConnect={() => requestConnect(\"transfer\")}\n            onDeposited={(txHash) => {\n              onRelayDeposited?.(txHash);\n              emit({ name: \"success\", action: \"transfer\", txHash });\n            }}\n            onError={(error) => {\n              onRelayError?.(error);\n              emit({\n                name: \"failure\",\n                action: \"transfer\",\n                message: error.message,\n              });\n            }}\n          >\n            <RelayDeposit.Content />\n          </RelayDeposit>\n        )}\n      </div>\n    </TrackingBoundary>\n  );\n}\n","\"use client\";\n\n// The compound root: `<RelayDeposit>` + `.Trigger` + `.Content`, assembling\n// Tasks 3-10 into the user-facing deposit modal. This file owns wiring plus one\n// piece of state: the modal's OPEN flag — every prop goes into\n// `RelayDepositContext` (so `.Content`, supplied by the host as a child, can\n// read them) and the shell knobs go into `ResponsiveShell` (radix Dialog on\n// desktop, vaul Drawer on mobile).\n//\n// Why `open` lives here rather than in `ResponsiveShell`: the wizard's state\n// host must be mounted for exactly as long as the modal is open, and it sits\n// ABOVE the shell (see RelayWizardStateHost.tsx) — so the flag has to be known\n// above the shell too. `ResponsiveShell` keeps its own controlled/uncontrolled\n// handling for other callers (e.g. `<RelayHistory>`, which has no wizard state\n// to preserve and stays a plain pass-through); this root simply always drives\n// it as fully controlled.\n//\n// Compound shape mirrors `WalletConnectModal`\n// (`packages/dapp-ui/src/features/wallet-connect-modal/components/WalletConnectModal.tsx`):\n// an `Object.assign` on the root function so `<RelayDeposit.Trigger>` /\n// `<RelayDeposit.Content>` are reachable without extra imports.\nimport * as React from \"react\";\n\nimport { RelayDepositContext } from \"../providers/relay-deposit-provider\";\nimport type { RelayDepositProps } from \"../types\";\nimport { RelayDepositContent } from \"./RelayDepositContent\";\nimport { RelayDepositTrigger } from \"./RelayDepositTrigger\";\nimport { RelayWizardStateHost } from \"./RelayWizardStateHost\";\nimport { ResponsiveShell } from \"./shell/ResponsiveShell\";\nimport \"../styles/component.css\";\n\nfunction RelayDepositRoot(props: RelayDepositProps) {\n  const {\n    theme = \"dark\",\n    mobileBreakpoint,\n    drawerDirection,\n    dialogWidth,\n    drawerMaxWidth,\n    drawerMinWidth,\n    style,\n    open: openProp,\n    onOpenChange,\n    children,\n  } = props;\n\n  // Same controlled/uncontrolled contract as before, just resolved one level\n  // higher: an `open` prop wins outright, and `onOpenChange` is notified either\n  // way (moved verbatim out of ResponsiveShell).\n  const [internalOpen, setInternalOpen] = React.useState(false);\n  const isControlled = openProp !== undefined;\n  const open = isControlled ? openProp : internalOpen;\n\n  const handleOpenChange = React.useCallback(\n    (value: boolean) => {\n      if (!isControlled) setInternalOpen(value);\n      onOpenChange?.(value);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  return (\n    // `props` is a fresh object each render, so consumers re-render exactly when\n    // this root does -- i.e. the same cadence they'd get from prop drilling.\n    // Memoizing it would only pretend to be stable (every field is a prop).\n    <RelayDepositContext.Provider value={props}>\n      <RelayWizardStateHost open={open}>\n        <ResponsiveShell\n          theme={theme}\n          mobileBreakpoint={mobileBreakpoint}\n          drawerDirection={drawerDirection}\n          dialogWidth={dialogWidth}\n          drawerMaxWidth={drawerMaxWidth}\n          drawerMinWidth={drawerMinWidth}\n          style={style}\n          open={open}\n          onOpenChange={handleOpenChange}\n        >\n          {children}\n        </ResponsiveShell>\n      </RelayWizardStateHost>\n    </RelayDepositContext.Provider>\n  );\n}\n\nexport const RelayDeposit = Object.assign(RelayDepositRoot, {\n  Trigger: RelayDepositTrigger,\n  Content: RelayDepositContent,\n});\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { RelayDepositProps } from \"../types\";\n\n/**\n * Carries `<RelayDeposit>`'s own configuration (client/recipient/amount/wallet\n * capabilities/callbacks) from the compound root down to\n * `<RelayDeposit.Content>`, which is supplied by the host as a child and so\n * can't receive them as props.\n *\n * Deliberately separate from `RelayShellContext`\n * (`./relay-shell-provider.tsx`): that one is the *shell's* concern (open\n * state, viewport, theme, portal style) and is shared with any future\n * relay compound built on `ResponsiveShell`, while this one is the deposit\n * widget's data. Mirrors how `WalletConnectModal` keeps its wallet registry\n * and its modal state in a single context only because that component has no\n * second compound — the relay shell already has two consumers.\n */\nexport const RelayDepositContext = createContext<RelayDepositProps | null>(null);\n\nexport function useRelayDepositContext(): RelayDepositProps {\n  const ctx = useContext(RelayDepositContext);\n  if (!ctx) {\n    throw new Error(\n      \"RelayDeposit compound components must be used within <RelayDeposit>\",\n    );\n  }\n  return ctx;\n}\n","\"use client\";\n\n// The deposit modal's rendering half. Every value it draws comes from\n// `RelayWizardContext`'s bundle (published by `RelayWizardEngine`, which lives\n// ABOVE the shell -- see RelayWizardStateHost.tsx) plus the shell context\n// (theme / viewport / close) and `<RelayDeposit>`'s own props; this file owns no\n// state and calls no data hook, which is exactly what lets radix `Dialog` and\n// vaul `Drawer` swap under it LIVE at the 768px breakpoint without losing the\n// user's place. Transient DOM state (input focus, scroll offset) still resets on\n// such a swap -- the nodes are genuinely new -- but the typed amount, selected\n// origin/target, the fetched deposit address and in-flight tx tracking do not.\n//\n// 2026-07-31 (user decision): this is ONE screen, not a 2-step wizard. The\n// asset selectors and the QR/address live in a single scroll column, so there\n// is no step state, no stepper, no Continue/Back, and no per-deposit\n// full-screen waiting/success step. Manual sends are observed indirectly via\n// the orders SSE + 1s REST re-fetch and rendered in a separate status card.\n// See DepositPanel.tsx (which replaced\n// steps/SourceStep + steps/ExecuteStep) for the full rationale.\n//\n// Layout/markup ported from relay-protocol\n// packages/relay-widget/src/components/RelayDeposit.tsx (v0.1.0): same section\n// order and recovery aggregation, hosted inside dapp-ui's modal shell.\n// Differences from the source widget, all deliberate:\n//\n//   * wallet capabilities arrive as injected props (RelayWalletProps) rather\n//     than wagmi hooks, and are handed to useDepositAddress as `wallet`;\n//   * the full `OrdersList` is gone, while its SSE invalidation hook is shared\n//     with the inline deposit-arrival monitor and recovery panel;\n//   * the theme comes from the shell context (`data-theme` on `.rd-root`),\n//     which has no 'auto' option — hosts pick light or dark explicitly.\nimport * as React from \"react\";\n\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport { useRelayDepositContext } from \"../providers/relay-deposit-provider\";\nimport { useRelayShellContext } from \"../providers/relay-shell-provider\";\nimport {\n  useRelayWizardBundle,\n  type RelayDepositView,\n} from \"../providers/relay-wizard-provider\";\nimport { DepositPanel } from \"./DepositPanel\";\nimport { DepositAddressSkeleton } from \"./DepositStepView\";\nimport { HistoryEmptyState, OrdersList } from \"./OrdersList\";\nimport { RecoverFunds } from \"./RecoverFunds\";\nimport { ShellContent } from \"./shell/ResponsiveShell\";\nimport \"../styles/component.css\";\n\nfunction DepositTabs({\n  id,\n  activeView,\n  recoverableCount,\n  onChange,\n}: {\n  id: string;\n  activeView: RelayDepositView;\n  recoverableCount: number;\n  onChange: (view: RelayDepositView) => void;\n}) {\n  const tabs: readonly RelayDepositView[] = [\"deposit\", \"history\", \"recover\"];\n\n  return (\n    <div\n      className=\"rd-deposit-tabs\"\n      role=\"tablist\"\n      aria-label=\"Deposit navigation\"\n      data-tab-count={tabs.length}\n      data-active-index={tabs.indexOf(activeView)}\n    >\n      {tabs.map((view) => {\n        const label = view === \"deposit\" ? \"Deposit\" : view === \"history\" ? \"History\" : \"Recover\";\n        return (\n          <button\n            key={view}\n            type=\"button\"\n            id={`${id}-${view}-tab`}\n            className=\"rd-deposit-tab\"\n            role=\"tab\"\n            aria-selected={activeView === view}\n            aria-controls={`${id}-${view}-panel`}\n            tabIndex={activeView === view ? 0 : -1}\n            data-active={activeView === view ? \"true\" : \"false\"}\n            data-track={`${view}_tab`}\n            onClick={() => onChange(view)}\n            onKeyDown={(event) => {\n              if (![\"ArrowLeft\", \"ArrowRight\", \"Home\", \"End\"].includes(event.key)) return;\n              event.preventDefault();\n              const currentIndex = tabs.indexOf(view);\n              const nextIndex =\n                event.key === \"Home\"\n                  ? 0\n                  : event.key === \"End\"\n                    ? tabs.length - 1\n                    : (currentIndex + (event.key === \"ArrowRight\" ? 1 : -1) + tabs.length) % tabs.length;\n              const nextView = tabs[nextIndex];\n              onChange(nextView);\n              document.getElementById(`${id}-${nextView}-tab`)?.focus();\n            }}\n          >\n            <span>{label}</span>\n            {view === \"recover\" && recoverableCount > 0 && (\n              <span className=\"rd-recovery-badge\" data-testid=\"recovery-badge\">\n                {recoverableCount}\n              </span>\n            )}\n          </button>\n        );\n      })}\n    </div>\n  );\n}\n\nexport interface RelayDepositContentProps {\n  /** Extra class name(s) for the dialog/drawer content element. Falls back to\n   * `<RelayDeposit>`'s own `className`. */\n  className?: string;\n}\n\n/**\n * `<RelayDeposit.Content>` — the modal/drawer surface. The deposit body is a\n * child of `ShellContent`, i.e. of radix's `Dialog.Portal` / vaul's\n * `Drawer.Portal`, which render nothing while closed. The state it renders is\n * NOT its own any more: `<RelayDeposit>` mounts the wizard engine above the\n * shell for exactly as long as the modal is open, so \"no request before open\"\n * and \"everything resets on close\" still hold -- while a breakpoint crossing\n * mid-flow now only rebuilds this DOM, not the flow.\n */\nexport function RelayDepositContent({ className }: RelayDepositContentProps) {\n  const props = useRelayDepositContext();\n\n  return (\n    <TrackingBoundary feature=\"relay\">\n      <ShellContent\n        ariaTitle=\"Deposit\"\n        className={[\"rd-deposit-shell\", className ?? props.className].filter(Boolean).join(\" \")}\n      >\n        <RelayDepositBody />\n      </ShellContent>\n    </TrackingBoundary>\n  );\n}\n\nfunction RelayDepositBody() {\n  const props = useRelayDepositContext();\n  const { theme } = useRelayShellContext();\n  const wizard = useRelayWizardBundle();\n  const tabsId = React.useId();\n\n  // No bundle means the engine isn't mounted, i.e. the modal is closed -- the\n  // portal above renders nothing in that state anyway, so this is only ever hit\n  // for the single pre-paint render of an opening modal (the engine publishes in\n  // a layout effect that lands before this subtree's own; see\n  // useRelayWizardBundle). Rendering nothing keeps that frame empty rather than\n  // half-built.\n  if (!wizard) return null;\n\n  const {\n    activeView,\n    setActiveView,\n    recoveryOpen,\n    resolvedApiBaseUrl,\n    wallet,\n    catalog,\n    catalogLoading,\n    catalogError,\n    origin,\n    setOrigin,\n    amount,\n    setAmount,\n    recipient,\n    setRecipient,\n    recipientValid,\n    balance,\n    balanceLoading,\n    insufficientBalance,\n    depositAddress,\n    depositAddressLoading,\n    depositAddressError,\n    arrivalStatus,\n    quote,\n    quoteLoading,\n    quoteError,\n    canDeposit,\n    deposit,\n    depositState,\n    targetOptions,\n    selectedTarget,\n    setSelectedTarget,\n    showAmountInput,\n    minNotice,\n    showRecipientInput,\n    showTargetSelector,\n    showOneClick,\n    showConnectPrompt,\n    showRecipientHint,\n    canRecover,\n    recoverableCount,\n    recoveryTargetsLoading,\n    recoveryTargets,\n    recoveryOrders,\n    historyOrdersLoading,\n    historyOrdersError,\n    refreshRecovery,\n    trustedFactories,\n    amountHint,\n    amountSmallestUnits,\n    onMax,\n  } = wizard;\n\n  return (\n    <div\n      className=\"rd-root rd-wizard\"\n      data-theme={theme}\n      data-rd-scroll-area\n    >\n      <DepositTabs\n        id={tabsId}\n        activeView={activeView}\n        recoverableCount={recoverableCount}\n        onChange={setActiveView}\n      />\n\n      {recoveryOpen && (\n        <div\n          className=\"rd-deposit-tab-panel\"\n          id={`${tabsId}-recover-panel`}\n          role=\"tabpanel\"\n          aria-labelledby={`${tabsId}-recover-tab`}\n        >\n          {canRecover ? (\n            <RecoverFunds\n              client={props.client}\n              apiBaseUrl={resolvedApiBaseUrl}\n              recipient={recipient}\n              target={selectedTarget?.symbol}\n              wallet={wallet}\n              trustedFactories={trustedFactories}\n              targets={(catalog?.targets.length ?? 0) > 0 ? recoveryTargets : undefined}\n              targetsLoading={recoveryTargetsLoading}\n              orders={recoveryOrders}\n              onRecovered={refreshRecovery}\n              onRecoverySuccess={props.onRecoverySuccess}\n              onError={props.onError}\n              showTitle={false}\n            />\n          ) : (\n            <div\n              className=\"rd-recover-empty\"\n              role=\"status\"\n              data-variant=\"unavailable\"\n              data-testid=\"rd-recovery-unavailable-note\"\n            >\n              <span className=\"rd-recover-empty-icon\" aria-hidden=\"true\">\n                <svg width=\"32\" height=\"32\" viewBox=\"0 0 24 24\" fill=\"none\" focusable=\"false\">\n                  <rect\n                    x=\"5\"\n                    y=\"10\"\n                    width=\"14\"\n                    height=\"10\"\n                    rx=\"2.5\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"1.7\"\n                  />\n                  <path\n                    d=\"M8 10V7.5a4 4 0 0 1 8 0V10\"\n                    stroke=\"currentColor\"\n                    strokeWidth=\"1.7\"\n                    strokeLinecap=\"round\"\n                  />\n                  <circle cx=\"12\" cy=\"15\" r=\"1.2\" fill=\"currentColor\" />\n                </svg>\n              </span>\n              <h3 className=\"rd-recover-empty-title\">Recovery unavailable</h3>\n              <p className=\"rd-recover-empty-copy\">\n                Connect a wallet that supports recovery transactions to continue.\n              </p>\n            </div>\n          )}\n        </div>\n      )}\n\n      {activeView === \"history\" && (\n        <div\n          className=\"rd-deposit-tab-panel\"\n          id={`${tabsId}-history-panel`}\n          role=\"tabpanel\"\n          aria-labelledby={`${tabsId}-history-tab`}\n        >\n          {recipientValid && recipient.trim() ? (\n            <OrdersList\n              orders={recoveryOrders}\n              isLoading={catalogLoading || historyOrdersLoading}\n              error={catalogError ?? historyOrdersError}\n              catalog={catalog}\n              showTitle={false}\n            />\n          ) : (\n            <HistoryEmptyState requiresIdentity />\n          )}\n        </div>\n      )}\n\n      {activeView === \"deposit\" && (\n        <div\n          className=\"rd-deposit-tab-panel\"\n          id={`${tabsId}-deposit-panel`}\n          role=\"tabpanel\"\n          aria-labelledby={`${tabsId}-deposit-tab`}\n        >\n\n      {/* Catalog skeleton: mirrors the settled panel's real block order and\n          dimensions. Footer/details below render in both states, so only the\n          data-driven body is represented here. */}\n      {catalogLoading && (\n        <div className=\"rd-skeleton-form\" data-testid=\"rd-source-skeleton\" aria-hidden=\"true\">\n          <DepositAddressSkeleton />\n\n          {showConnectPrompt && props.onRequestConnect && <span className=\"rd-skeleton rd-skeleton-cta\" />}\n\n          <section className=\"rd-stack\">\n            <div className=\"rd-field\">\n              <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"18%\" }} />\n              <div className=\"rd-field-row\">\n                <span className=\"rd-skeleton rd-skeleton-field\" />\n                <span className=\"rd-skeleton rd-skeleton-field\" />\n              </div>\n            </div>\n\n            {showAmountInput && (\n              <div className=\"rd-field\">\n                <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"34%\" }} />\n                <span className=\"rd-skeleton rd-skeleton-field\" />\n              </div>\n            )}\n          </section>\n\n          <div className=\"rd-field\">\n            <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"24%\" }} />\n            <span className=\"rd-skeleton rd-skeleton-field\" />\n            {(showRecipientInput || recipient.trim().length > 0) && (\n              <>\n                <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"58%\", marginTop: \"0.5rem\" }} />\n                <span className=\"rd-skeleton rd-skeleton-field\" />\n                {showRecipientInput && <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"82%\" }} />}\n              </>\n            )}\n          </div>\n\n          <span className=\"rd-skeleton rd-skeleton-notice\" />\n        </div>\n      )}\n      {catalogLoading && (\n        <p className=\"rd-sr-only\" role=\"status\">\n          Loading deposit options...\n        </p>\n      )}\n\n      {catalogError && (\n        <div className=\"rd-callout rd-callout-danger\">\n          Failed to load configuration: {catalogError.message}\n        </div>\n      )}\n\n      {/* (The connect-prompt card that used to sit here left with the\n          2026-07-31 \"A안 · QR 퍼스트\" layout: the disconnected state is now shown\n          where it actually bites -- a locked card IN THE QR SLOT plus the\n          `Connect Wallet` CTA, rendered by <DepositPanel> via `locked`. A card\n          above the form saying the same thing was the second copy of it, and it\n          pushed the (empty) address slot even further down.) */}\n\n      {showRecipientHint && (\n        <div className=\"rd-callout rd-callout-muted\">\n          {showRecipientInput\n            ? \"Connect a wallet or provide a recipient address to fetch your deposit address.\"\n            : \"Connect a wallet to fetch your deposit address.\"}\n        </div>\n      )}\n\n      {/* The whole body, one screen, QR first: context line + `change`\n          disclosure, optional amount + quote, the QR/address block (or the\n          locked card when there is no identity yet), the receive strip and the\n          send-rules checklist. `<DepositPanel>` renders the address section\n          itself (spinner line while loading, error callout on failure), so the\n          address disappearing mid-flow -- an origin/target change, a failed\n          refetch -- is explained in place. */}\n      {catalog && origin && (\n        <DepositPanel\n          catalog={catalog}\n          origin={origin}\n          onChangeOrigin={setOrigin}\n          amount={amount}\n          onChangeAmount={setAmount}\n          showAmountInput={showAmountInput}\n          // Passed in BOTH modes now: it feeds the checklist card, which states\n          // the effective minimum whether or not an amount input is shown (the\n          // amount input's own inline below-min/above-max advisory is\n          // `amountHint`, and is unrelated).\n          minNotice={minNotice}\n          amountHint={amountHint}\n          onMax={onMax}\n          recipient={recipient}\n          onChangeRecipient={setRecipient}\n          showRecipientInput={showRecipientInput}\n          recipientValid={recipientValid}\n          balance={balance}\n          balanceLoading={balanceLoading}\n          insufficientBalance={insufficientBalance}\n          targetOptions={targetOptions}\n          selectedTarget={selectedTarget}\n          onChangeTarget={setSelectedTarget}\n          showTargetSelector={showTargetSelector}\n          quote={quote}\n          quoteLoading={quoteLoading}\n          quoteError={quoteError}\n          depositAddress={depositAddress}\n          depositAddressLoading={depositAddressLoading}\n          depositAddressError={depositAddressError}\n          arrivalStatus={arrivalStatus}\n          amountSmallestUnits={amountSmallestUnits}\n          depositState={depositState}\n          onDeposit={() => void deposit()}\n          canDeposit={canDeposit && showOneClick}\n          oneClickEnabled={showOneClick}\n          locked={showConnectPrompt}\n          onRequestConnect={props.onRequestConnect}\n        />\n      )}\n        </div>\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\nimport { createContext, useContext, type CSSProperties } from \"react\";\nimport type { RelayDrawerDirection, RelayTheme } from \"../types\";\n\n/**\n * Context shared between `ResponsiveShell`, `ShellTrigger`, and `ShellContent`\n * — mirrors `WalletConnectModalContextValue`\n * (`packages/dapp-ui/src/features/wallet-connect-modal/providers/wallet-connect-modal-provider.ts`)\n * scoped down to only what the shared shell needs (compound components own\n * their own body content; the shell owns open state, viewport, and theme).\n *\n * `portalStyle` mirrors `WalletConnectModalContextValue.portalStyle`: radix's\n * `Dialog.Portal`/vaul's `Drawer.Portal` teleport their content to\n * `document.body`, outside the wrapper div's DOM subtree, so the merged\n * size-var style computed in `ResponsiveShell` must be re-applied directly on\n * the portaled `Dialog.Content`/`Drawer.Content` node — the wrapper's inline\n * style/CSS custom properties never reach it via cascade.\n */\nexport interface RelayShellContextValue {\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  isMobile: boolean;\n  theme: RelayTheme;\n  drawerDirection: RelayDrawerDirection;\n  portalStyle: CSSProperties;\n}\n\nexport const RelayShellContext = createContext<RelayShellContextValue | null>(\n  null,\n);\n\nexport function useRelayShellContext(): RelayShellContextValue {\n  const ctx = useContext(RelayShellContext);\n  if (!ctx) {\n    throw new Error(\n      \"ResponsiveShell compound components must be used within <ResponsiveShell>\",\n    );\n  }\n  return ctx;\n}\n","\"use client\";\n\n// The deposit wizard's state channel, and the reason a LIVE dialog<->drawer\n// switch can keep every bit of that state.\n//\n// Why a store instead of a plain context value: the wizard state must live\n// ABOVE `ResponsiveShell` (swapping radix `Dialog.Root` <-> vaul `Drawer.Root`\n// unmounts everything below it, portal content included), yet it must also be\n// MOUNTED ONLY WHILE THE MODAL IS OPEN -- that is what keeps \"no fetch before\n// open\" and \"reset on close\" true without a single reset effect. A component\n// that both wraps the shell and mounts conditionally is impossible: React\n// remounts a subtree whose parent element type changes, so a conditional\n// wrapper would tear the shell (and the host's `<RelayDeposit.Trigger>`) down\n// and back up on every open/close -- losing vaul's slide-out animation and\n// radix's focus-return-to-trigger.\n//\n// So the ALWAYS-mounted `RelayWizardStateHost` owns this store and renders the\n// state-owning engine as a SIBLING of the shell, mounted only while open. The\n// engine publishes its bundle into the store; `RelayDepositBody`, arbitrarily\n// deep inside whichever portal is currently mounted, subscribes. Sideways data\n// flow is exactly what a context value cannot do -- the provider sits above the\n// engine, so the engine can never change it.\nimport {\n  createContext,\n  useContext,\n  useLayoutEffect,\n  useState,\n  type Dispatch,\n  type SetStateAction,\n} from \"react\";\n\nimport type { DepositState } from \"../hooks/useDepositAddress\";\nimport type { Catalog, OrderSummary, OriginOption, QuoteResult, Target } from \"../lib/types\";\nimport type { RecoveryTargetEntry } from \"../lib/recoveryGroups\";\nimport type { RelayBalance, RelayWalletProps } from \"../types\";\n\nexport type DepositArrivalPhase =\n  | \"connecting\"\n  | \"waiting\"\n  | \"detected\"\n  | \"funded\"\n  | \"processing\"\n  | \"completed\"\n  | \"failed\"\n  | \"unavailable\";\n\nexport type RelayDepositView = \"deposit\" | \"history\" | \"recover\";\n\n/** Live projection of the current reusable forwarder's funding/delivery state.\n * SSE is only an invalidation signal; RelayWizardEngine derives this after\n * re-fetching /orders and, for the actual waiting balance, /recovery. */\nexport interface DepositArrivalStatus {\n  phase: DepositArrivalPhase;\n  /** Latest newly-attributed BSC-forwarder arrival, formatted in bridge-token\n   * human units. It is not the amount originally sent on the origin chain. */\n  amount?: string;\n  symbol?: string;\n  backendStatus?: string;\n}\n\n/**\n * Everything `RelayDepositBody` renders from — the whole of what used to be\n * computed inside the portal. Kept deliberately flat and pre-derived (every\n * `show*`/`can*` gate resolved) so the body stays render-only: no hook of its\n * own beyond the shell context (theme/isMobile/close) and this bundle.\n *\n * No step/navigation fields any more: the deposit body is ONE screen as of the\n * 2026-07-31 decision (see DepositPanel.tsx). `step`/`goToSource`/`goToExecute`\n * and the `canContinue` gate left with the Continue button; the only survivor of\n * that gate is `amountHint`, which now merely explains itself inline.\n */\nexport interface RelayWizardBundle {\n  // ---- Deposit modal tabs -----------------------------------------------\n  activeView: RelayDepositView;\n  setActiveView: Dispatch<SetStateAction<RelayDepositView>>;\n  /** Compatibility projection used by the recovery polling path. */\n  recoveryOpen: boolean;\n  setRecoveryOpen: Dispatch<SetStateAction<boolean>>;\n\n  // ---- Resolved configuration (shared by RecoverFunds + the fetches) ----\n  resolvedApiBaseUrl: string;\n  wallet: RelayWalletProps;\n\n  // ---- useDepositAddress ------------------------------------------------\n  catalog?: Catalog;\n  catalogLoading: boolean;\n  catalogError?: Error;\n  origin?: OriginOption;\n  setOrigin: (origin: OriginOption) => void;\n  amount: string;\n  setAmount: (value: string) => void;\n  recipient: string;\n  setRecipient: (value: string) => void;\n  recipientValid: boolean;\n  balance?: RelayBalance & { formatted: string };\n  balanceLoading: boolean;\n  insufficientBalance: boolean;\n  depositAddress?: string;\n  depositAddressLoading: boolean;\n  depositAddressError?: Error;\n  /** SSE + fallback-poll projection for the address currently on screen. */\n  arrivalStatus: DepositArrivalStatus;\n  quote?: QuoteResult;\n  quoteLoading: boolean;\n  quoteError?: Error;\n  /** The hook's own gate, unchanged -- the body still ANDs it with `showOneClick`. */\n  canDeposit: boolean;\n  deposit: () => Promise<void>;\n  depositState: DepositState;\n\n  // ---- useTargetTokens --------------------------------------------------\n  targetOptions: Target[];\n  selectedTarget?: Target;\n  setSelectedTarget: (symbol: string) => void;\n\n  // ---- Pre-derived render gates ----------------------------------------\n  showAmountInput: boolean;\n  /** Prominent minimum-deposit guidance shown when the amount input is hidden\n   * (`hideAmount`) -- the meta line under the input can't carry it then. */\n  minNotice?: {\n    min: string;\n    symbol: string;\n    /** Human amount already sitting un-swept at the user's forwarder from an\n     * earlier below-minimum deposit, in the RECOVERY token's decimals. Set only\n     * together with `remaining` (i.e. only when it doesn't yet clear `min`). */\n    pending?: string;\n    /** Symbol for `pending`, when the backend reports one -- omitted rather\n     * than guessed (`RecoveryInfo` has no top-level symbol field). */\n    pendingSymbol?: string;\n    /** What the NEXT deposit still has to cover: `min` minus `pending`, both\n     * rescaled to the origin token's decimals. Presence is what switches the\n     * notice to its \"accumulating\" copy. */\n    remaining?: string;\n  };\n  showRecipientInput: boolean;\n  showTargetSelector: boolean;\n  showOneClick: boolean;\n  showConnectPrompt: boolean;\n  showRecipientHint: boolean;\n  canRecover: boolean;\n  recoverableCount: number;\n  recoveryTargetsLoading: boolean;\n  /** Recovery info per catalog target (successful fetches only), in catalog\n   * order -- <RecoverFunds>' per-forwarder mode input (upstream e5bff75).\n   * Empty when the catalog has no targets: the panel then self-fetches. */\n  recoveryTargets: RecoveryTargetEntry[];\n  /** The user's orders, shared by History, the live deposit monitor, and the\n   * recovery panel. There is exactly one orders subscription per open modal. */\n  recoveryOrders: OrderSummary[];\n  historyOrdersLoading: boolean;\n  historyOrdersError?: Error;\n  /** Refetches every target's recovery info (bumped after a recovery\n   * attempt changes on-chain state). */\n  refreshRecovery: () => void;\n  /** The effective trusted-factory allowlist: the host's explicit prop, else\n   * the CDN apps.json `relay.forwarderFactories` values (which replace the pinned\n   * defaults when valid -- see mergeTrustedFactories). Threaded to\n   * <RecoverFunds> so the one-click deposit and the recovery panel verify\n   * against the same list. */\n  trustedFactories?: readonly string[];\n  /** Inline advisory under the amount input: the entered amount is below the\n   * effective minimum or above the ops-set maximum. Gates NOTHING -- the deposit\n   * address is minted from (recipient, origin, target) alone, and the address is\n   * reusable, so an out-of-range amount is a hint about what to send, not a\n   * blocker. (This is what `canContinue`/`continueBlockedHint` collapsed into\n   * when the Continue button went away.) */\n  amountHint?: string;\n  amountSmallestUnits?: string;\n  /** \"Max\" affordance, or undefined when there is no spendable balance to fill in. */\n  onMax?: () => void;\n}\n\ntype Listener = () => void;\n\nexport interface RelayWizardStore {\n  subscribe(listener: Listener): () => void;\n  /** The currently published bundle, or null while the modal is closed. */\n  getSnapshot(): RelayWizardBundle | null;\n  publish(next: RelayWizardBundle | null): void;\n}\n\nexport function createRelayWizardStore(): RelayWizardStore {\n  let snapshot: RelayWizardBundle | null = null;\n  const listeners = new Set<Listener>();\n\n  return {\n    getSnapshot: () => snapshot,\n    publish(next) {\n      if (next === snapshot) return;\n      snapshot = next;\n      // Copied before iterating: a listener that unsubscribes while being\n      // notified (a body unmounting on the same commit) must not skip the rest.\n      for (const listener of [...listeners]) listener();\n    },\n    subscribe(listener) {\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n      };\n    },\n  };\n}\n\nexport const RelayWizardStoreContext = createContext<RelayWizardStore | null>(null);\n\nexport function useRelayWizardStore(): RelayWizardStore {\n  const store = useContext(RelayWizardStoreContext);\n  if (!store) {\n    throw new Error(\"RelayDeposit compound components must be used within <RelayDeposit>\");\n  }\n  return store;\n}\n\n/**\n * Subscribes to the published wizard bundle. Null means \"nothing published\n * yet\", which for `RelayDepositBody` only ever happens on a closed modal (the\n * engine publishes in a layout effect that runs BEFORE this one — sibling\n * fibers complete in order — so the very first body render inside a freshly\n * opened portal already has a bundle by the time it commits; a body remounted\n * by a dialog<->drawer swap reads the live snapshot straight out of `useState`).\n *\n * Deliberately not `useSyncExternalStore`: that one subscribes in a PASSIVE\n * effect, so a bundle published in the engine's layout effect would only reach\n * the body after paint — one visibly empty frame on every open.\n */\nexport function useRelayWizardBundle(): RelayWizardBundle | null {\n  const store = useRelayWizardStore();\n  const [bundle, setBundle] = useState(store.getSnapshot);\n\n  useLayoutEffect(() => {\n    // Catches a publish that landed between this component's render and this\n    // effect (i.e. the engine's own layout effect, on the first open).\n    setBundle(store.getSnapshot());\n    return store.subscribe(() => setBundle(store.getSnapshot()));\n  }, [store]);\n\n  return bundle;\n}\n","import styleInject from '#style-inject';styleInject(\".rd-root{--rd-bg: var(--ds-bg-dim, #f7f8fb);--rd-surface: var(--ds-surface-default, #ffffff);--rd-surface-alt: var(--ds-surface-medium, #f0f1f6);--rd-border: var(--ds-border-default, #e2e4ec);--rd-text: var(--ds-content-highest, #14161f);--rd-text-muted: var(--ds-content-medium, #676b7a);--rd-accent: var(--ds-accent-primary-high, #5b5bf6);--rd-accent-contrast: var(--ds-accent-primary-on, #ffffff);--rd-success: var(--ds-system-green-default, #1e9e6a);--rd-success-bg: var(--ds-system-green-low, #e6f7f0);--rd-warning: #b8860b;--rd-warning-bg: #fdf3d9;--rd-danger: var(--ds-system-red-default, #d1394a);--rd-danger-bg: var(--ds-system-red-low, #fbe8ea);--rd-radius: 14px;--rd-shadow: var(--ds-shadow-modal, 0 1px 2px rgba(20, 22, 31, .04), 0 8px 24px rgba(20, 22, 31, .06));--rd-on-accent: #ffffff;width:100%;max-width:640px;box-sizing:border-box;margin:0 auto;display:flex;flex-direction:column;gap:1.25rem;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--rd-bg);color:var(--rd-text);-webkit-font-smoothing:antialiased;color-scheme:light dark}.rd-root[data-theme=dark]{color-scheme:dark;--rd-bg: var(--ds-bg-default, #0f1117);--rd-surface: var(--ds-surface-default, #171923);--rd-surface-alt: var(--ds-surface-medium, #1e212c);--rd-border: var(--ds-border-default, #2a2d3a);--rd-text: var(--ds-content-highest, #eef0f6);--rd-text-muted: var(--ds-content-medium, #9498a8);--rd-accent: var(--ds-accent-primary-high, #8181ff);--rd-accent-contrast: var(--ds-accent-primary-on, #0f1117);--rd-success: var(--ds-system-green-default, #3fd68f);--rd-success-bg: var(--ds-system-green-low, #123626);--rd-warning: #e0b23c;--rd-warning-bg: #3a2f10;--rd-danger: var(--ds-system-red-default, #ff6b7a);--rd-danger-bg: var(--ds-system-red-low, #3a1418);--rd-shadow: var(--ds-shadow-modal, 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .35))}.rd-root[data-theme=light]{color-scheme:light;--rd-bg: var(--ds-bg-dim, #f7f8fb);--rd-surface: var(--ds-surface-default, #ffffff);--rd-surface-alt: var(--ds-surface-medium, #f0f1f6);--rd-border: var(--ds-border-default, #e2e4ec);--rd-text: var(--ds-content-highest, #14161f);--rd-text-muted: var(--ds-content-medium, #676b7a);--rd-accent: var(--ds-accent-primary-high, #5b5bf6);--rd-accent-contrast: var(--ds-accent-primary-on, #ffffff);--rd-success: var(--ds-system-green-default, #1e9e6a);--rd-success-bg: var(--ds-system-green-low, #e6f7f0);--rd-warning: #b8860b;--rd-warning-bg: #fdf3d9;--rd-danger: var(--ds-system-red-default, #d1394a);--rd-danger-bg: var(--ds-system-red-low, #fbe8ea);--rd-radius: 14px;--rd-shadow: var(--ds-shadow-modal, 0 1px 2px rgba(20, 22, 31, .04), 0 8px 24px rgba(20, 22, 31, .06))}.rd-root *{box-sizing:border-box}.rd-card{background:var(--rd-surface);border:1px solid var(--rd-border);border-radius:var(--rd-radius);box-shadow:var(--rd-shadow);padding:1.25rem;display:flex;flex-direction:column;gap:.9rem}.rd-card-header{display:flex;align-items:baseline;justify-content:space-between;gap:.5rem}.rd-card-title{font-size:1rem;font-weight:650;margin:0}.rd-card-hint{font-size:.8rem;color:var(--rd-text-muted)}.rd-token-address{display:flex;flex-direction:column;gap:.1rem}.rd-token-address-addr{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.78rem;color:var(--rd-text);overflow-wrap:anywhere;user-select:all}.rd-token-address-meta{font-size:.72rem;color:var(--rd-text-muted)}.rd-field{display:flex;flex-direction:column;gap:.35rem}.rd-field-label{font-size:.69rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--rd-text-muted)}.rd-root .rd-field-label-row{display:flex;align-items:baseline;justify-content:space-between;gap:.5rem}.rd-root .rd-token-explorer-link{font-size:.75rem;font-weight:500;color:var(--rd-accent);text-decoration:none;white-space:nowrap}.rd-root .rd-token-explorer-link:hover{text-decoration:underline}.rd-field-row{display:grid;grid-template-columns:1fr 1fr;gap:.625rem}.rd-root select,.rd-root input[type=text],.rd-root input[type=number]{width:100%;padding:.6rem .7rem;border-radius:12px;border:1px solid var(--rd-border);background:var(--rd-surface-alt);color:var(--rd-text);font-size:16px;font-family:inherit}.rd-root select:focus,.rd-root input:focus{outline:2px solid var(--rd-accent);outline-offset:1px}.rd-dropdown{position:relative;min-width:0}.rd-root .rd-dropdown-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.55rem .7rem;border-radius:12px;border:1px solid var(--rd-border);background:var(--rd-surface-alt);color:var(--rd-text);font-size:.9rem;font-family:inherit;font-weight:600;text-align:left;cursor:pointer}.rd-root .rd-static-target{display:flex;align-items:center;gap:.55rem;padding:.55rem .7rem;border-radius:12px;border:1px solid var(--rd-border);background:var(--rd-surface-alt);color:var(--rd-text);font-size:.9rem;font-weight:600;min-width:0;overflow-wrap:anywhere}.rd-root .rd-dropdown-trigger:disabled{cursor:not-allowed}.rd-dropdown-trigger:focus,.rd-dropdown-trigger:focus-visible{outline:2px solid var(--rd-accent);outline-offset:1px}.rd-dropdown-trigger-content{display:flex;align-items:center;gap:.55rem;min-width:0}.rd-dropdown-trigger-label{display:flex;flex-direction:column;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.2}.rd-dropdown-trigger-sub{font-size:.66rem;font-weight:500;line-height:1.3;color:var(--rd-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rd-dropdown-chevron{display:inline-flex;flex:none;color:var(--rd-text-muted);line-height:1}.rd-dropdown-popover{z-index:100002;width:var(--radix-popover-trigger-width);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;--rd-surface: var(--ds-surface-default, #ffffff);--rd-surface-alt: var(--ds-surface-medium, #f0f1f6);--rd-border: var(--ds-border-default, #e2e4ec);--rd-text: var(--ds-content-highest, #14161f);--rd-accent: var(--ds-system-orange-default, #5b5bf6);--rd-shadow: var(--ds-shadow-modal, 0 1px 2px rgba(20, 22, 31, .04), 0 8px 24px rgba(20, 22, 31, .06))}.rd-dropdown-popover[data-theme=dark]{color-scheme:dark;--rd-surface: var(--ds-surface-default, #171923);--rd-surface-alt: var(--ds-surface-medium, #1e212c);--rd-border: var(--ds-border-default, #2a2d3a);--rd-text: var(--ds-content-highest, #eef0f6);--rd-accent: var(--ds-system-orange-default, #8181ff);--rd-shadow: var(--ds-shadow-modal, 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px rgba(0, 0, 0, .35))}.rd-dropdown-popover *{box-sizing:border-box}.rd-dropdown-listbox{margin:0;padding:.3rem;list-style:none;max-height:min(15rem,var(--radix-popover-content-available-height, 15rem));overflow-y:auto;overscroll-behavior:contain;background:var(--rd-surface);border:1px solid var(--rd-border);border-radius:10px;box-shadow:var(--rd-shadow);font-family:inherit;color:var(--rd-text)}.rd-dropdown-option{display:flex;align-items:center;gap:.55rem;padding:.5rem .6rem;border-radius:8px;font-size:.9rem;color:var(--rd-text);cursor:pointer}.rd-dropdown-option-active{background:var(--rd-surface-alt)}.rd-dropdown-option-selected{font-weight:650}.rd-dropdown-option-label{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rd-dropdown-option-check{flex:none;color:var(--rd-accent);font-size:.8rem}.rd-amount-input{display:flex;align-items:baseline;gap:.5rem;padding:.6rem .75rem;border-radius:12px;border:1px solid var(--rd-border);background:var(--rd-surface-alt)}.rd-amount-input:focus-within{outline:2px solid var(--rd-accent);outline-offset:1px}.rd-amount-input[data-rd-invalid]{border-color:var(--rd-danger)}.rd-root .rd-amount-input input[type=text]{flex:1 1 auto;width:auto;min-width:0;padding:0;border:0;border-radius:0;background:none;font-size:22px;font-weight:700;font-variant-numeric:tabular-nums;line-height:1.2}.rd-root .rd-amount-input input[type=text]:focus{outline:none}.rd-amount-input-suffix{flex:none;font-size:.8rem;font-weight:700;color:var(--rd-text-muted);pointer-events:none}.rd-root .rd-amount-input-max{flex:none;padding:0;font-size:.69rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--rd-accent);background:transparent;border:0;border-radius:0;cursor:pointer;line-height:1}.rd-amount-input-max:hover:not(:disabled){text-decoration:underline}.rd-amount-input-max:disabled{opacity:.5;cursor:not-allowed}.rd-amount-meta{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;font-size:.75rem;color:var(--rd-text-muted);padding:0 .15rem;font-variant-numeric:tabular-nums}.rd-root button{font-family:inherit;font-size:.95rem;font-weight:650;border-radius:10px;border:1px solid transparent;padding:.7rem 1.1rem;cursor:pointer;transition:opacity .12s ease,transform .12s ease}.rd-root button:active:not(:disabled){transform:translateY(1px)}.rd-root button:disabled{cursor:not-allowed;opacity:.55}.rd-button-primary{background:var(--rd-accent);color:var(--rd-accent-contrast)}.rd-button-secondary{background:var(--rd-surface-alt);color:var(--rd-text);border-color:var(--rd-border)}.rd-button-block{width:100%}.rd-callout{border-radius:10px;padding:.65rem .85rem;font-size:.85rem;line-height:1.4}.rd-callout-warning{background:var(--rd-warning-bg);color:var(--rd-warning)}.rd-callout-danger{background:var(--rd-danger-bg);color:var(--rd-danger)}.rd-callout-success{background:var(--rd-success-bg);color:var(--rd-success)}.rd-callout-muted{background:var(--rd-surface-alt);color:var(--rd-text-muted)}.rd-quote-strip{display:flex;align-items:center;justify-content:space-between;gap:.625rem;padding:.7rem .85rem;border-radius:14px;background:color-mix(in srgb,var(--rd-accent) 10%,var(--rd-surface));border:1px solid color-mix(in srgb,var(--rd-accent) 35%,transparent)}.rd-quote-strip-side{display:flex;flex-direction:column;min-width:0;gap:.1rem;font-size:.9rem;font-weight:700;color:var(--rd-text);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.rd-quote-strip-end{text-align:right;align-items:flex-end}.rd-quote-strip-side small{font-size:.69rem;font-weight:600;color:var(--rd-text-muted);letter-spacing:.02em}.rd-quote-strip-arrow{flex:none;color:var(--rd-accent);font-weight:800}.rd-quote-strip-hint{font-size:.72rem;line-height:1.45;color:var(--rd-text-muted);padding:0 .15rem}.rd-deposit-address{display:grid;grid-template-columns:minmax(0,1fr) auto;grid-template-rows:auto 1fr auto;align-items:start;gap:.7rem;min-height:172px;background:var(--rd-surface);border:1px solid var(--rd-border);border-radius:16px;box-shadow:var(--rd-shadow);padding:.85rem;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;color:var(--rd-text-muted)}.rd-deposit-address-label{grid-column:1;grid-row:1;position:relative;display:inline-flex;align-items:center;gap:.35rem;width:fit-content;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif}.rd-root button.rd-address-term{position:static;width:auto;height:auto;display:inline;padding:0 0 2px;border:0;border-bottom:1px dotted currentColor;border-radius:0;background:transparent;color:var(--rd-text-muted);font:700 .69rem/1 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;letter-spacing:.08em;text-transform:uppercase}.rd-root button.rd-address-term:hover,.rd-root button.rd-address-term:focus-visible{color:var(--rd-accent);outline:none}.rd-address-tooltip{position:absolute;z-index:20;top:calc(100% + 8px);left:0;width:min(250px,calc(100vw - 48px));padding:9px 11px;border:1px solid var(--rd-border);border-radius:10px;background:var(--rd-surface);box-shadow:var(--rd-shadow);color:var(--rd-text);font-size:11.5px;font-weight:500;line-height:1.45;letter-spacing:normal;text-align:left;text-transform:none;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(-3px);transition:opacity .12s ease,transform .12s ease,visibility .12s ease}.rd-address-term:hover .rd-address-tooltip,.rd-address-term:focus .rd-address-tooltip,.rd-address-term:focus-visible .rd-address-tooltip{opacity:1;visibility:visible;transform:translateY(0)}.rd-address-full{grid-column:1 / -1;grid-row:2;align-self:center;min-width:0;color:var(--rd-text);font-weight:600;line-height:1.55;overflow-wrap:anywhere;word-break:break-all}.rd-root .rd-address-copy{grid-column:1 / -1;grid-row:3;width:100%;min-height:36px;display:inline-flex;align-items:center;justify-content:center;gap:.4rem;margin:0;padding:.5rem .7rem;border-radius:10px;border:0;background:var(--rd-accent);color:var(--rd-accent-contrast);font-family:inherit;font-size:.75rem;font-weight:800;letter-spacing:.03em;line-height:1}.rd-root .rd-address-copy svg{flex:none}.rd-root .rd-address-copy:hover:not(:disabled){opacity:.92}.rd-root button.rd-mobile-qr-toggle,.rd-root button.rd-mobile-address-toggle{display:none;place-items:center;width:34px;height:34px;padding:0;border:1px solid color-mix(in srgb,var(--rd-accent) 24%,var(--rd-border));border-radius:11px;background:color-mix(in srgb,var(--rd-accent) 10%,var(--rd-surface));color:var(--rd-accent);box-shadow:0 1px 2px color-mix(in srgb,var(--rd-text) 8%,transparent);transition:transform .15s ease,background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.rd-root button.rd-mobile-qr-toggle{grid-column:2;grid-row:1;justify-self:end;align-self:start}.rd-root button.rd-mobile-address-toggle{position:absolute;z-index:2;top:.85rem;right:.85rem;background:var(--rd-surface);color:var(--rd-text-muted);box-shadow:0 4px 14px color-mix(in srgb,var(--rd-text) 16%,transparent)}.rd-root button.rd-mobile-qr-toggle:hover:not(:disabled),.rd-root button.rd-mobile-address-toggle:hover:not(:disabled){border-color:color-mix(in srgb,var(--rd-accent) 45%,var(--rd-border));color:var(--rd-accent);background:color-mix(in srgb,var(--rd-accent) 15%,var(--rd-surface));box-shadow:0 5px 16px color-mix(in srgb,var(--rd-accent) 18%,transparent);transform:translateY(-1px)}.rd-root button.rd-mobile-qr-toggle:active:not(:disabled),.rd-root button.rd-mobile-address-toggle:active:not(:disabled){transform:scale(.96)}.rd-deposit-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:.9rem;container:rd-deposit-head / inline-size}.rd-deposit-head>.rd-deposit-qr-pane{flex:none}.rd-deposit-head>.rd-deposit-address{flex:1 1 12rem;min-width:0}@container rd-deposit-head (max-width: 23.75rem){.rd-deposit-head>.rd-deposit-address{flex-basis:100%;width:100%}.rd-deposit-head>.rd-skeleton-qr{display:none}.rd-deposit-head>.rd-skeleton-address{flex-basis:100%;width:100%}.rd-root button.rd-mobile-qr-toggle,.rd-root button.rd-mobile-address-toggle{display:grid}.rd-deposit-head[data-compact-view=address]>.rd-deposit-qr-pane,.rd-deposit-head[data-compact-view=qr]>.rd-deposit-address{display:none}.rd-deposit-head[data-compact-view=address]>.rd-deposit-address,.rd-deposit-head[data-compact-view=qr]>.rd-deposit-qr-pane{animation:rd-compact-pane-in .26s cubic-bezier(.22,1,.36,1)}.rd-deposit-head[data-compact-view=qr]>.rd-deposit-qr-pane{width:100%}}@keyframes rd-compact-pane-in{0%{opacity:0;transform:translateY(6px) scale(.99)}to{opacity:1;transform:translateY(0) scale(1)}}.rd-deposit-qr{position:relative;display:grid;place-items:center;width:172px;height:172px;background:#fff;border-radius:16px;border:1px solid var(--rd-border);box-shadow:var(--rd-shadow);margin:0 auto}.rd-deposit-qr-pane{position:relative;display:grid;place-items:center;min-height:172px}.rd-deposit-qr-badge{position:absolute;inset:0;margin:auto;width:36px;height:36px;display:grid;place-items:center;background:#fff;border-radius:50%;pointer-events:none}.rd-stepper{display:flex;flex-direction:column;gap:0}.rd-stepper-item{display:flex;gap:.75rem;position:relative;padding-bottom:1.25rem}.rd-stepper-item:last-child{padding-bottom:0}.rd-stepper-rail{display:flex;flex-direction:column;align-items:center}.rd-stepper-dot{width:22px;height:22px;border-radius:50%;border:2px solid var(--rd-border);background:var(--rd-surface);display:flex;align-items:center;justify-content:center;font-size:.7rem;font-weight:700;flex:none;color:var(--rd-text-muted)}.rd-stepper-item-done .rd-stepper-dot{background:var(--rd-success);border-color:var(--rd-success);color:var(--rd-on-accent)}.rd-stepper-item-active .rd-stepper-dot{border-color:var(--rd-accent);color:var(--rd-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--rd-accent) 20%,transparent)}.rd-stepper-line{flex:1;width:2px;background:var(--rd-border);margin:2px 0}.rd-stepper-item-done .rd-stepper-line{background:var(--rd-success)}.rd-stepper-body{padding-top:1px}.rd-stepper-label{font-weight:600;font-size:.9rem}.rd-stepper-item-pending .rd-stepper-label{color:var(--rd-text-muted)}.rd-stepper-meta{margin-top:.25rem;font-size:.78rem;color:var(--rd-text-muted);display:flex;flex-direction:column;gap:.15rem}.rd-stepper-meta a{color:var(--rd-accent);text-decoration:none}.rd-stepper-meta a:hover{text-decoration:underline}.rd-status-pill{display:inline-flex;align-items:center;gap:.35rem;font-size:.75rem;font-weight:650;padding:.2rem .55rem;border-radius:999px;background:var(--rd-surface-alt);color:var(--rd-text-muted);width:fit-content}.rd-status-pill-success{background:var(--rd-success-bg);color:var(--rd-success)}.rd-status-pill-failure{background:var(--rd-danger-bg);color:var(--rd-danger)}.rd-spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:rd-spin .7s linear infinite;vertical-align:-.15em}@keyframes rd-spin{to{transform:rotate(360deg)}}.rd-muted{color:var(--rd-text-muted)}.rd-settings-row{display:flex;justify-content:flex-end}.rd-root .rd-icon-button{position:relative;display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;padding:0;border-radius:50%;border:1px solid var(--rd-border);background:var(--rd-surface);color:var(--rd-text-muted)}.rd-root .rd-icon-button:hover:not(:disabled){color:var(--rd-text);background:var(--rd-surface-alt)}.rd-recovery-badge{position:absolute;top:-.3rem;right:-.3rem;display:flex;align-items:center;justify-content:center;min-width:1.05rem;height:1.05rem;padding:0 .25rem;border-radius:999px;background:var(--rd-danger, #d1394a);color:#fff;font-size:.65rem;font-weight:700;line-height:1;border:2px solid var(--rd-surface, #ffffff)}.rd-recover-group{display:flex;flex-direction:column;gap:.75rem}.rd-recover-group+.rd-recover-group{border-top:1px solid var(--rd-border);padding-top:1rem;margin-top:.5rem}.rd-recover-group-label{margin:0;font-size:.8rem;color:var(--rd-text-muted)}.rd-recover-made-of{display:block;padding:.65rem .8rem;background:var(--rd-surface-alt);border:1px solid var(--rd-border);border-radius:10px}.rd-recover-made-of-label{cursor:pointer;font-size:.72rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--rd-text-muted)}.rd-recover-made-of-body{display:flex;flex-direction:column;gap:.4rem;margin-top:.6rem}.rd-error-details{margin-top:-.4rem;padding:0 .15rem;color:var(--rd-text-muted);font-size:.72rem}.rd-error-details summary{width:max-content;cursor:pointer;list-style:none;font-weight:600}.rd-error-details summary::-webkit-details-marker{display:none}.rd-error-details summary:before{display:inline-block;margin-right:.35rem;content:\\\"\\\\203a\\\";transition:transform .12s ease}.rd-error-details[open] summary:before{transform:rotate(90deg)}.rd-error-log{position:relative;margin-top:.5rem}.rd-root button.rd-error-copy{position:absolute;z-index:1;top:.45rem;right:.45rem;display:inline-flex;align-items:center;justify-content:center;width:1.65rem;height:1.65rem;padding:0;border:1px solid var(--rd-border);border-radius:6px;background:var(--rd-surface);color:var(--rd-text-muted)}.rd-root button.rd-error-copy:hover:not(:disabled){background:var(--rd-surface-alt);color:var(--rd-text)}.rd-error-details code{display:block;max-height:6rem;overflow:auto;padding:.6rem 2.7rem .6rem .7rem;border:1px solid var(--rd-border);border-radius:8px;background:var(--rd-surface-alt);color:var(--rd-text-muted);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.68rem;line-height:1.45;overflow-wrap:anywhere;scrollbar-color:var(--rd-border) transparent;scrollbar-width:thin;white-space:pre-wrap}.rd-error-details code::-webkit-scrollbar{width:4px;height:4px}.rd-error-details code::-webkit-scrollbar-thumb{border-radius:999px;background:var(--rd-border)}.rd-token-list{display:flex;flex-direction:column;gap:.5rem}.rd-token-row{display:flex;align-items:center;gap:10px;background:var(--rd-surface-alt);border:1px solid var(--rd-border);border-radius:12px;padding:12px 14px}.rd-token-row-info{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;overflow-wrap:anywhere}.rd-token-row-symbol{font-weight:700}.rd-token-row-amount{font-variant-numeric:tabular-nums;font-size:14px;font-weight:700;color:var(--rd-text);white-space:nowrap}.rd-token-row-status{font-size:11.5px;line-height:1.45;color:var(--rd-text-muted)}.rd-token-row-warn{font-size:11.5px;line-height:1.45;color:var(--rd-warning)}.rd-token-row-warn b{font-weight:750;font-variant-numeric:tabular-nums}.rd-token-row-step{font-size:11.5px;line-height:1.45;font-weight:650;color:var(--rd-accent)}.rd-root .rd-token-row-action{flex:none;width:auto;padding:9px 16px;font-size:13px;font-weight:700}.rd-token-row-spinner{flex:none;font-size:18px;color:var(--rd-accent)}.rd-recover-sum{display:flex;flex-direction:column;align-items:center;gap:4px;text-align:center;background:var(--rd-surface-alt);border:1px solid var(--rd-border);border-radius:12px;padding:16px 14px;container:rd-recover-sum / inline-size}.rd-recover-sum-label{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rd-text-muted)}.rd-recover-sum-value{max-width:100%;overflow:hidden;font-size:22px;font-size:clamp(18px,7cqi,28px);font-weight:800;line-height:1.15;font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;color:var(--rd-text)}.rd-recover-sum-unit{display:block;margin:.35rem 0 0;font-size:15px;font-weight:700;color:var(--rd-text-muted)}.rd-recover-sum-empty{font-size:17px;font-weight:700;color:var(--rd-text-muted)}.rd-recover-empty{display:flex;min-height:min(20rem,48vh);flex:1 1 auto;flex-direction:column;align-items:center;justify-content:center;gap:.7rem;padding:2.5rem 1.5rem;border:1px solid var(--rd-border);border-radius:16px;background:radial-gradient(circle at 50% 0%,color-mix(in srgb,var(--rd-accent) 11%,transparent),transparent 48%),var(--rd-surface-alt);text-align:center;box-sizing:border-box}.rd-recover-empty-icon{display:grid;width:4.25rem;height:4.25rem;place-items:center;margin-bottom:.2rem;border:1px solid color-mix(in srgb,var(--rd-accent) 30%,var(--rd-border));border-radius:50%;background:color-mix(in srgb,var(--rd-accent) 12%,var(--rd-surface));color:var(--rd-accent);box-shadow:0 10px 30px color-mix(in srgb,var(--rd-accent) 12%,transparent)}.rd-recover-empty[data-variant=unavailable] .rd-recover-empty-icon{border-color:var(--rd-border);background:var(--rd-surface);color:var(--rd-text-muted);box-shadow:none}.rd-recover-empty-title,.rd-recover-sum-value.rd-recover-empty-title{max-width:100%;margin:0;overflow:visible;color:var(--rd-text);font-size:1.15rem;font-weight:750;line-height:1.35;white-space:normal}.rd-recover-empty-copy{max-width:23rem;margin:0;color:var(--rd-text-muted);font-size:.82rem;line-height:1.55;text-wrap:balance}.rd-root button.rd-recover-empty-refresh{display:inline-flex;min-height:2.6rem;align-items:center;justify-content:center;gap:.45rem;margin-top:.45rem;padding:.65rem 1rem;color:var(--rd-text);font-size:.82rem}.rd-root button.rd-recover-empty-refresh:hover:not(:disabled){border-color:color-mix(in srgb,var(--rd-accent) 35%,var(--rd-border));background:color-mix(in srgb,var(--rd-accent) 9%,var(--rd-surface));color:var(--rd-accent)}[data-rd-recover-funds].rd-stack{flex:1 1 auto}.rd-recover-foot{margin-top:auto;padding-top:.5rem;text-align:center;font-size:11.5px;line-height:1.45;color:var(--rd-text-muted)}.rd-history-table{display:flex;min-width:0;flex-direction:column;gap:.6rem;font-size:.875rem}.rd-history-empty{display:flex;min-height:min(20rem,48vh);flex:1 1 auto;flex-direction:column;align-items:center;justify-content:center;gap:.7rem;padding:2.5rem 1.5rem;border:1px solid var(--rd-border);border-radius:16px;background:radial-gradient(circle at 50% 0%,color-mix(in srgb,var(--rd-accent) 10%,transparent),transparent 48%),var(--rd-surface-alt);text-align:center;box-sizing:border-box}.rd-history-empty-icon{display:grid;width:4.25rem;height:4.25rem;place-items:center;margin-bottom:.2rem;border:1px solid color-mix(in srgb,var(--rd-accent) 30%,var(--rd-border));border-radius:50%;background:color-mix(in srgb,var(--rd-accent) 12%,var(--rd-surface));color:var(--rd-accent);box-shadow:0 10px 30px color-mix(in srgb,var(--rd-accent) 12%,transparent)}.rd-history-empty[data-variant=disconnected] .rd-history-empty-icon{border-color:var(--rd-border);background:var(--rd-surface);color:var(--rd-text-muted);box-shadow:none}.rd-history-empty-title{margin:0;color:var(--rd-text);font-size:1.15rem;font-weight:750;line-height:1.35}.rd-history-empty-copy{max-width:22rem;margin:0;color:var(--rd-text-muted);font-size:.82rem;line-height:1.55;text-wrap:balance}.rd-history-empty-live{display:inline-flex;align-items:center;gap:.4rem;margin-top:.35rem;color:var(--rd-text-muted);font-size:.72rem;font-weight:650}.rd-history-empty-live>span{width:.45rem;height:.45rem;border-radius:50%;background:var(--rd-success);box-shadow:0 0 0 3px color-mix(in srgb,var(--rd-success) 15%,transparent)}.rd-history-group{min-width:0;max-width:100%;background:var(--rd-surface-alt);border:1px solid var(--rd-border);border-radius:12px;overflow:hidden}.rd-history-row-button{width:100%;min-width:0;display:flex;flex-direction:column;gap:.75rem;padding:1rem;background:none;border:none;border-radius:inherit;color:inherit;font:inherit;text-align:left;cursor:pointer}.rd-history-row-button[aria-expanded=true]{border-radius:12px 12px 0 0}.rd-history-row-button:hover{background:color-mix(in srgb,var(--rd-border) 30%,transparent)}.rd-history-row-button:focus-visible{outline:2px solid var(--rd-accent);outline-offset:-2px}.rd-history-card-top{display:flex;align-items:center;gap:.5rem;min-width:0}.rd-history-card-top>.rd-status-pill{margin-left:auto;flex:none}.rd-history-card-top>.rd-status-pill:not(.rd-status-pill-success):not(.rd-status-pill-failure){background:color-mix(in srgb,var(--rd-accent) 12%,transparent);color:var(--rd-accent)}.rd-history-asset{font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rd-history-card-amounts{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:.75rem;padding:.75rem;border-radius:10px;background:var(--rd-surface)}.rd-history-amount-block{display:flex;max-width:100%;flex-direction:column;gap:.15rem;min-width:0}.rd-history-amount-block:last-child{align-items:flex-end;text-align:right}.rd-history-amount-label{color:var(--rd-text-muted);font-size:.68rem;font-weight:650;line-height:1.2}.rd-history-num{max-width:100%;overflow:hidden;font-variant-numeric:tabular-nums;font-size:.875rem;font-weight:700;text-overflow:ellipsis;white-space:nowrap}.rd-history-num[data-full-value],.rd-history-step-amount[data-full-value]{cursor:help}[data-rd-history-floating-tooltip]{position:fixed;z-index:100100;width:max-content;max-width:calc(100vw - 1.5rem);padding:.45rem .65rem;border:1px solid;border-radius:8px;font-size:.72rem;font-weight:600;line-height:1.35;text-align:left;white-space:normal;overflow-wrap:anywhere;pointer-events:none}.rd-history-card-arrow{display:inline-flex;align-items:center;justify-content:center;color:var(--rd-text-muted);font-size:.85rem}.rd-history-received{color:var(--rd-success)}.rd-history-received-estimate{color:var(--rd-text-muted);font-weight:600}.rd-history-card-foot{display:flex;align-items:center;justify-content:space-between;gap:.5rem}.rd-history-date{color:var(--rd-text-muted);font-size:.78rem;font-variant-numeric:tabular-nums}.rd-history-chevron{display:inline-flex;flex:none;align-items:center;justify-content:center;color:var(--rd-text-muted);text-align:center;transition:transform .16s ease}.rd-history-row-button[aria-expanded=true] .rd-history-chevron{transform:rotate(180deg)}.rd-history-detail{display:flex;min-width:0;flex-direction:column;gap:.375rem;padding:.65rem .9rem .8rem;border-top:1px solid var(--rd-border)}.rd-history-step{display:grid;min-width:0;grid-template-columns:.5rem auto 1fr auto auto;gap:.5rem;align-items:center}.rd-history-step-dot{width:.5rem;height:.5rem;border-radius:50%;background:var(--rd-border)}.rd-history-step-dot-done{background:var(--rd-success)}.rd-history-step-label{font-weight:600}.rd-history-step-chain{color:var(--rd-text-muted)}.rd-history-step-amount{font-variant-numeric:tabular-nums;color:var(--rd-text-muted)}.rd-history-step-tx{font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.rd-history-step-shared{margin-left:.4rem;color:var(--rd-text-muted);font-size:.72rem}.rd-skeleton{border-radius:10px;background:linear-gradient(90deg,var(--rd-surface-alt) 25%,color-mix(in srgb,var(--rd-surface-alt) 55%,var(--rd-surface)) 42%,var(--rd-surface-alt) 60%);background-size:220% 100%;animation:rd-skeleton-wave 1.4s ease-in-out infinite}@keyframes rd-skeleton-wave{0%{background-position:130% 0}to{background-position:-90% 0}}@media(prefers-reduced-motion:reduce){.rd-skeleton,.rd-checklist-status,.rd-deposit-head[data-compact-view=address]>.rd-deposit-address,.rd-deposit-head[data-compact-view=qr]>.rd-deposit-qr-pane,.rd-checklist-status-waiting .rd-checklist-status-dot,.rd-checklist-status-detected .rd-checklist-status-dot{animation:none}}.rd-skeleton-form{display:flex;flex-direction:column;gap:.85rem}.rd-skeleton-qr{flex:none;width:172px;height:172px;border-radius:16px}.rd-skeleton-address{flex:1 1 12rem;min-width:0;height:172px;border-radius:16px}.rd-skeleton-line{height:.9rem;width:40%}.rd-skeleton-field{height:48px}.rd-skeleton-notice{height:96px}.rd-skeleton-cta{height:48px}.rd-wizard{flex:1 1 auto;min-height:0;overflow-y:auto;container:rd-wizard / inline-size;padding:1rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom,0px));scroll-padding:1.5rem 0 calc(1.5rem + env(safe-area-inset-bottom,0px));overscroll-behavior:contain;-webkit-overflow-scrolling:touch;max-width:none;gap:.85rem}.rd-deposit-tabs{--rd-tab-count: 3;--rd-active-tab-index: 0;position:sticky;top:-1rem;z-index:2;display:flex;flex:0 0 auto;justify-content:space-around;margin:-1rem -1.25rem 0;padding:1rem 1.25rem 0;border-bottom:1px solid var(--rd-border);background:var(--rd-surface)}.rd-deposit-tabs[data-active-index=\\\"1\\\"]{--rd-active-tab-index: 1}.rd-deposit-tabs[data-active-index=\\\"2\\\"]{--rd-active-tab-index: 2}.rd-deposit-tabs:after{position:absolute;bottom:-1px;left:1.25rem;width:calc((100% - 2.5rem) / var(--rd-tab-count));height:2px;background:var(--rd-accent);content:\\\"\\\";pointer-events:none;transform:translate(calc(var(--rd-active-tab-index) * 100%));transition:transform .22s cubic-bezier(.22,1,.36,1),width .18s ease}.rd-root .rd-deposit-tab{position:relative;display:inline-flex;flex:1 1 0;min-width:0;min-height:2.75rem;align-items:center;justify-content:center;gap:.4rem;padding:.85rem .35rem;border:0;border-bottom:2px solid transparent;border-radius:0;background:transparent;color:var(--rd-text-muted);font:inherit;font-size:.875rem;font-weight:650;line-height:1.2;transition:border-color .16s ease,background-color .16s ease,color .16s ease}.rd-root .rd-deposit-tab[data-active=true]{color:var(--rd-text)}.rd-root .rd-deposit-tab:hover:not(:disabled),.rd-root .rd-deposit-tab:focus-visible{outline:none;background:color-mix(in srgb,var(--rd-accent) 7%,transparent);color:var(--rd-text)}.rd-deposit-tab>.rd-recovery-badge{position:static;flex:none;min-width:1rem;height:1rem;padding:0 .2rem;border-width:0}.rd-deposit-tab-panel{display:flex;min-width:0;flex:1 1 auto;flex-direction:column;gap:.85rem;transform-origin:top center;animation:rd-deposit-tab-in .18s cubic-bezier(.22,1,.36,1)}@keyframes rd-deposit-tab-in{0%{opacity:0;transform:translateY(.35rem)}to{opacity:1;transform:translateY(0)}}.rd-stack{display:flex;min-width:0;flex-direction:column;gap:.75rem}.rd-wizard-header{display:flex;align-items:center;gap:.5rem;margin-bottom:0}.rd-wizard-heading{font-size:1rem;font-weight:700;color:var(--rd-text);margin:0}.rd-wizard-actions{margin-left:auto;display:flex;align-items:center;gap:.25rem}.rd-min-notice-pending{color:var(--rd-text-muted)}.rd-min-notice-pending b{font-weight:750;color:var(--rd-warning);font-variant-numeric:tabular-nums}.rd-locked-card{display:flex;width:100%;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1.5rem 1.25rem;border:1px solid var(--rd-border);border-radius:16px;background:radial-gradient(circle at 50% 0%,color-mix(in srgb,var(--rd-accent) 12%,transparent),transparent 52%),var(--rd-surface-alt);text-align:center;box-sizing:border-box}.rd-locked-glyph{display:grid;width:3rem;height:3rem;place-items:center;margin-bottom:.1rem;border:1px solid color-mix(in srgb,var(--rd-accent) 30%,var(--rd-border));border-radius:50%;background:color-mix(in srgb,var(--rd-accent) 12%,var(--rd-surface));color:var(--rd-accent);line-height:0;box-shadow:0 10px 30px color-mix(in srgb,var(--rd-accent) 12%,transparent)}.rd-locked-title{margin:0;color:var(--rd-text);font-size:1rem;font-weight:750;line-height:1.3}.rd-locked-text{max-width:22rem;margin:0;color:var(--rd-text-muted);font-size:.8rem;line-height:1.45}.rd-locked-card>.rd-card-hint{max-width:22rem;margin:-.2rem 0 0;text-wrap:balance}.rd-root button.rd-locked-action{display:inline-flex;width:min(18rem,100%);min-height:2.6rem;align-items:center;justify-content:center;gap:.5rem;margin-top:.25rem}.rd-delivery-recipient{display:flex;flex-direction:column;gap:.3rem;margin-top:.5rem}.rd-checklist-credit{font-size:clamp(10px,2.7vw,11.5px);white-space:nowrap}.rd-checklist{background:var(--rd-surface);border:1px solid var(--rd-border);border-radius:14px;padding:11px 14px;display:flex;flex-direction:column;gap:7px}.rd-checklist-row{display:flex;align-items:flex-start;gap:.5rem;font-size:12.5px;line-height:1.5;color:var(--rd-text)}.rd-checklist-row[data-rd-checklist-only]{align-items:center;white-space:nowrap;font-size:clamp(10.5px,2.8vw,12.5px)}.rd-checklist-mark{flex:none;width:1.05rem;text-align:center;font-weight:800;color:var(--rd-text-muted)}.rd-checklist-mark-warn{color:var(--rd-warning)}.rd-checklist-accent{color:var(--rd-accent);font-weight:750;font-variant-numeric:tabular-nums}.rd-checklist-status{min-height:54px;display:flex;align-items:center;gap:.65rem;padding:10px 12px;border:1px solid color-mix(in srgb,var(--rd-accent) 30%,var(--rd-border));border-radius:12px;background:var(--rd-surface-alt);color:var(--rd-text-muted);font-size:12.5px;line-height:1.45;transform-origin:top center;animation:rd-status-enter .48s cubic-bezier(.22,1,.36,1)}.rd-checklist-status-waiting,.rd-checklist-status-detected{border-color:color-mix(in srgb,var(--rd-warning) 42%,var(--rd-border));box-shadow:0 0 0 1px color-mix(in srgb,var(--rd-warning) 10%,transparent)}@keyframes rd-status-enter{0%{opacity:0;transform:translateY(-10px) scale(.985);box-shadow:0 0 color-mix(in srgb,var(--rd-accent) 28%,transparent)}65%{opacity:1;transform:translateY(1px) scale(1);box-shadow:0 0 0 4px color-mix(in srgb,var(--rd-accent) 16%,transparent)}to{opacity:1;transform:translateY(0);box-shadow:0 0 0 0 transparent}}.rd-checklist-status-dot{flex:none;width:9px;height:9px;border-radius:50%;background:var(--rd-warning);box-shadow:0 0 0 4px color-mix(in srgb,var(--rd-warning) 14%,transparent)}.rd-checklist-status-waiting .rd-checklist-status-dot,.rd-checklist-status-detected .rd-checklist-status-dot{animation:rd-status-breathe 1.8s ease-in-out infinite}@keyframes rd-status-breathe{0%,to{opacity:.82;transform:scale(.96);box-shadow:0 0 0 3px color-mix(in srgb,var(--rd-warning) 10%,transparent)}50%{opacity:1;transform:scale(1);box-shadow:0 0 0 5px color-mix(in srgb,var(--rd-warning) 15%,transparent)}}.rd-checklist-status-funded,.rd-checklist-status-processing{color:var(--rd-text);border-color:color-mix(in srgb,var(--rd-accent) 40%,transparent)}.rd-checklist-status-funded .rd-checklist-status-dot,.rd-checklist-status-processing .rd-checklist-status-dot{background:var(--rd-accent);box-shadow:0 0 0 4px color-mix(in srgb,var(--rd-accent) 14%,transparent)}.rd-checklist-status-completed{border-color:color-mix(in srgb,var(--rd-success) 40%,transparent);background:var(--rd-success-bg);color:var(--rd-success)}.rd-checklist-status-completed .rd-checklist-status-dot{background:var(--rd-success);box-shadow:0 0 0 4px color-mix(in srgb,var(--rd-success) 14%,transparent)}.rd-checklist-status-failed{border-color:color-mix(in srgb,var(--rd-danger) 40%,transparent);background:var(--rd-danger-bg);color:var(--rd-danger)}.rd-checklist-status-failed .rd-checklist-status-dot{background:var(--rd-danger);box-shadow:0 0 0 4px color-mix(in srgb,var(--rd-danger) 14%,transparent)}@container rd-wizard (max-width: 30rem){.rd-recover-sum{padding-inline:.75rem}.rd-recover-sum-value{font-size:clamp(1rem,6cqi,1.5rem)}[data-rd-recover-funds] .rd-callout,.rd-token-row-status,.rd-token-row-warn,.rd-token-row-step,.rd-recover-foot{overflow-wrap:anywhere}.rd-token-row{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;padding:.875rem}.rd-root .rd-token-row-action{width:100%;min-height:2.75rem;grid-column:1 / -1}.rd-token-row-spinner{grid-column:1 / -1;justify-self:center}}@container rd-wizard (max-width: 24rem){.rd-history-card-top{flex-direction:column;align-items:flex-start}.rd-history-card-top>.rd-status-pill{max-width:100%;margin-left:0;white-space:normal}.rd-history-asset{overflow:visible;text-overflow:clip;white-space:normal}.rd-history-card-amounts{grid-template-columns:minmax(0,1fr);gap:.45rem}.rd-history-amount-block{width:100%}.rd-history-amount-block:last-child{align-items:flex-start;text-align:left}.rd-history-card-arrow{display:inline-flex;justify-self:center;margin:0;transform:rotate(90deg);transform-origin:center}.rd-history-card-foot{align-items:flex-start}.rd-history-date{min-width:0;line-height:1.4;overflow-wrap:anywhere}.rd-history-step{grid-template-columns:.5rem minmax(0,1fr);grid-template-areas:\\\"dot label\\\" \\\". chain\\\" \\\". amount\\\" \\\". tx\\\";column-gap:.5rem;row-gap:.25rem;align-items:start;padding-block:.65rem;line-height:1.35}.rd-history-step+.rd-history-step{border-top:1px solid var(--rd-border)}.rd-history-step-dot{grid-area:dot;margin-top:.35rem}.rd-history-step-label{grid-area:label}.rd-history-step-chain{grid-area:chain}.rd-history-step-amount{grid-area:amount}.rd-history-step-tx{grid-area:tx;min-width:0;max-width:100%}.rd-history-step-tx a{overflow-wrap:anywhere}.rd-history-step-shared{display:block;margin:.15rem 0 0}.rd-history-step>.rd-muted{grid-area:tx}}@container rd-wizard (max-width: 22rem){.rd-root .rd-deposit-tab{gap:.2rem;padding-inline:.15rem;font-size:.8rem}.rd-history-row-button,.rd-history-detail{padding-inline:.75rem}}@media(prefers-reduced-motion:reduce){.rd-deposit-tabs:after,.rd-root .rd-deposit-tab{transition:none}.rd-deposit-tab-panel{animation:none}}\\n\")","\"use client\";\n\n// The dynamic transfer-status card plus the separate two-line send-rules\n// checklist. The status is rendered only when useful; the rules always remain.\n// The rules consolidate\n// everything the user must not get wrong. Replaces `MinDepositNotice.tsx` (the\n// \"Send at least N\" block) AND `DepositStepView`'s standalone \"Only send X on Y\"\n// warning callout, per the 2026-07-31 \"A안 · QR 퍼스트\" one-page redesign:\n//\n//   !  Base USDT only · 6 USDT minimum\n//   ↻  Reusable — deposit as many times as you like\n//\n// Why consolidate: the rules were spread across three surfaces (a minimum block\n// above the address, a warning callout below it, and a \"reusable address\"\n// sentence between them), each styled as its own alarm. Two short lines in one\n// quiet card are read; stacked callouts are skipped.\n//\n// The PENDING variant adds the amber row this file inherited from\n// MinDepositNotice (`.rd-min-notice-pending`): when an earlier below-minimum\n// deposit is still sitting un-swept at the user's forwarder, the sweep fires on\n// the COMBINED balance, so the row names both what is already waiting and what is\n// still missing -- asking for the full minimum again would make the user\n// over-send.\nimport \"../styles/component.css\";\nimport type { DepositArrivalStatus } from \"../providers/relay-wizard-provider\";\n\nexport interface MinNoticeData {\n  min: string;\n  symbol: string;\n  /** An earlier below-minimum deposit still waiting un-swept at the user's\n   * forwarder. `remaining` present = show the dynamic amber row. */\n  pending?: string;\n  pendingSymbol?: string;\n  remaining?: string;\n}\n\nexport function DepositStatus({\n  minNotice,\n  arrivalStatus,\n}: {\n  minNotice: MinNoticeData;\n  /** SSE-derived status enriches the pending-balance message once a new\n   * deposit is detected. */\n  arrivalStatus: DepositArrivalStatus;\n}) {\n  const arrived =\n    arrivalStatus.amount && arrivalStatus.symbol\n      ? `${arrivalStatus.amount} ${arrivalStatus.symbol} arrived`\n      : arrivalStatus.amount\n        ? `${arrivalStatus.amount} arrived`\n        : undefined;\n  const liveMessage = (() => {\n    switch (arrivalStatus.phase) {\n      case \"detected\":\n        if (minNotice.remaining !== undefined) {\n          const remaining = `${minNotice.remaining} ${minNotice.symbol}`;\n          return arrived\n            ? `${arrived} at the processing address. Waiting for ${remaining} more to reach the minimum.`\n            : `Deposit detected. Waiting for ${remaining} more to reach the minimum.`;\n        }\n        return arrived\n          ? `${arrived} at the processing address. Checking the minimum.`\n          : \"Deposit detected. Checking the minimum.\";\n      case \"funded\":\n        return `${minNotice.min} ${minNotice.symbol} received at the processing address. Preparing delivery.`;\n      case \"processing\":\n        return \"Minimum reached. Bridging and delivering to ONEchain.\";\n      case \"completed\":\n        return \"Transfer completed. Funds were delivered to ONEchain.\";\n      case \"failed\":\n        return `Transfer needs attention${\n          arrivalStatus.backendStatus ? ` (${arrivalStatus.backendStatus})` : \"\"\n        }.`;\n      case \"unavailable\":\n        return minNotice.remaining === undefined\n          ? \"Live status is temporarily unavailable. Retrying automatically.\"\n          : undefined;\n      // 아직 아무 입금도 감지되지 않은 평시 상태. 여기서 undefined를 주면\n      // 상태 카드 자리가 통째로 빈칸으로 남는다 — 무엇을 기다리는 중인지\n      // 적어두는 편이 낫다는 사용자 요청(2026-08-06).\n      case \"connecting\":\n      case \"waiting\":\n      default:\n        // 미달 입금이 대기 중이면 아래 pending 행이 더 구체적이므로 양보한다.\n        return minNotice.remaining === undefined\n          ? \"Waiting for your deposit.\"\n          : undefined;\n    }\n  })();\n\n  return liveMessage ? (\n    <div\n      key={`live:${arrivalStatus.phase}:${liveMessage}`}\n      className={`rd-checklist-status rd-checklist-status-${arrivalStatus.phase}`}\n      data-rd-deposit-status=\"\"\n      data-phase={arrivalStatus.phase}\n      role=\"status\"\n      aria-live=\"polite\"\n      aria-atomic=\"true\"\n    >\n      <span className=\"rd-checklist-status-dot\" aria-hidden=\"true\" />\n      <span>{liveMessage}</span>\n    </div>\n  ) : minNotice.remaining !== undefined ? (\n    <div\n      key={`pending:${minNotice.remaining}:${minNotice.symbol}`}\n      className=\"rd-checklist-status rd-checklist-status-waiting rd-min-notice-pending\"\n      data-rd-min-notice-pending=\"\"\n      role=\"status\"\n      aria-live=\"polite\"\n      aria-atomic=\"true\"\n    >\n      <span className=\"rd-checklist-status-dot\" aria-hidden=\"true\" />\n      <span>\n        Waiting for{\" \"}\n        <b>\n          {minNotice.remaining} {minNotice.symbol}\n        </b>{\" \"}\n        more to arrive at the processing address.\n      </span>\n    </div>\n  ) : null;\n}\n\nexport function DepositChecklist({\n  minNotice,\n  onlySend,\n  showWalletCreditNote = false,\n}: {\n  minNotice: MinNoticeData;\n  /** The selected origin, for the \"X on Y only\" line -- the wrong-token/wrong-chain\n   * rule this card absorbed from the old `.rd-address-warning` callout. */\n  onlySend: { symbol: string; chainName: string };\n  /** Fixed-recipient mode only: explains the hidden destination address\n   * without printing the address itself. */\n  showWalletCreditNote?: boolean;\n}) {\n  return (\n    <div className=\"rd-checklist\" data-rd-checklist=\"\">\n      <div className=\"rd-checklist-row\" data-rd-checklist-only=\"\">\n        <span className=\"rd-checklist-mark rd-checklist-mark-warn\" aria-hidden=\"true\">\n          !\n        </span>\n        <span>\n          <b>\n            {onlySend.chainName} {onlySend.symbol} only\n          </b>\n          {\" · \"}\n          <b className=\"rd-checklist-accent\">\n            {minNotice.min} {minNotice.symbol} minimum\n          </b>\n        </span>\n      </div>\n\n      <div className=\"rd-checklist-row\">\n        <span className=\"rd-checklist-mark\" aria-hidden=\"true\">\n          ↻\n        </span>\n        <span>Reusable — deposit as many times as you like</span>\n      </div>\n\n      {showWalletCreditNote && (\n        <div className=\"rd-checklist-row rd-checklist-credit\" data-rd-checklist-credit=\"\">\n          <span className=\"rd-checklist-mark\" aria-hidden=\"true\">\n            →\n          </span>\n          <span>Credited to your ONEchain wallet</span>\n        </div>\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/components/DepositStep.tsx\n// (v0.1.0), renamed DepositStepView.tsx to avoid a name collision with Task\n// 11's wizard step of the same concept. Mechanical substitution only: CSS\n// Modules `styles.x` references become literal \"rd-x\" class names (see\n// ../styles/component.css's header mapping table), the CSS Modules import\n// becomes the plain side-effect import below, viem's `formatUnits`/\n// `zeroAddress` are replaced by ../lib/units's self-implemented equivalents\n// (dapp-ui may depend on neither wagmi nor viem), `ChainKind` now comes from\n// ../lib/types, and `DepositState` now comes from ../hooks/useDepositAddress\n// (the ported hook, which exports the same shape). `qrcode.react`'s\n// `QRCodeSVG` import is unchanged -- already a dapp-ui dependency. Everything\n// else -- props, EIP-681/Solana Pay URI building, copy-to-clipboard, and\n// layout -- is the original logic, untouched.\n//\n// Behavior: svm (Solana) origins render a display-only deposit -- no\n// one-click button ever, a `solana:` Solana Pay QR, and (when present) the\n// exact human amount. EVM origins get a one-click deposit button ONLY when\n// `canDeposit` is true (i.e. the caller entered a usable amount) -- an\n// amount-less \"send anything\" deposit has nothing for a wallet tx to carry,\n// so it always falls back to the manual QR/address flow below.\n//\n// 2026-07-30 redesign (approved step-2 \"variant A\"), presentation only -- same\n// props, same URI building, same copy handler, same gating:\n//   * the QR is the hero: a 172px white frame near the top of the step instead\n//     of buried under two explainer paragraphs, which is what a user scanning\n//     with a phone actually looks for;\n//   * the \"Deposit address\" card title and the `.rd-card` wrapper are gone (the\n//     progress bar names the step now, and the block IS the step);\n//   * the address chip keeps its mono styling and accent Copy button;\n//   * the two explainer paragraphs collapse into ONE line under the address,\n//     and the long wrong-token warning into one sentence plus a collapsed\n//     `<details>` (\"More about this address\") holding the original wording.\n//\n// 2026-07-30 follow-up: the address is rendered IN FULL again. The redesign\n// middle-truncated it to `0x432f…b3159` to keep it on one mono line, but a\n// deposit address is the one value on this screen a user needs to VERIFY\n// character-by-character against what their exchange/wallet shows -- an\n// abbreviation they have to hover or copy to check defeats that. It now wraps\n// across two lines instead (`word-break: break-all` on the chip's text), which\n// costs a line of height and nothing else: the accent Copy button and the\n// full-address clipboard write are unchanged, and `title`/aria-label stay for\n// AT and for hosts reading the value off the DOM.\n//\n// 2026-07-31 one-page redesign: the SEND RULES left this file for\n// `DepositChecklist.tsx`, which states all three of them once, in a card under\n// the address. Gone from here with them: the `.rd-address-warning` callout (\"Only\n// send X on Y\") and its `hideTokenWarning` prop, the `minAmount` prop's \"deposits\n// below N may not be swept\" hint, and the amount-less \"reusable address -- send\n// any amount\" sentence. The long \"More about this address\" wording now lives in\n// the info tooltip beside the Deposit address label, so it stays attached to\n// the value it explains without reserving a detached block at the bottom.\nimport { useEffect, useId, useState } from \"react\";\nimport { formatUnits, zeroAddress } from \"../lib/units\";\nimport { ChainIcon } from \"./icons\";\nimport type { ChainKind } from \"../lib/types\";\nimport type { DepositState } from \"../hooks/useDepositAddress\";\nimport \"../styles/component.css\";\nimport { QRCodeSVG } from \"qrcode.react\";\n\nexport interface DepositStepViewProps {\n  depositAddress: string;\n  originChainId: number;\n  originChainName: string;\n  /** Token contract address, or the zero address for the chain's native asset. */\n  originCurrency: string;\n  /** Smallest-unit base-10 integer string, or undefined/\"\" when no amount was entered --\n   * this is a reusable address, so the deposit works with or without one. */\n  amount?: string;\n  tokenSymbol: string;\n  /** Origin token decimals -- used to render the exact human amount to send. */\n  tokenDecimals: number;\n  /** Origin chain VM family; \"svm\" (Solana) renders a display-only deposit. */\n  kind?: ChainKind;\n  /** EVM one-click deposit state, from useDepositAddress's `depositState`. */\n  depositState: DepositState;\n  /** EVM one-click deposit action, from useDepositAddress's `deposit`. No-op for svm origins. */\n  onDeposit: () => void;\n  /** True only when a usable amount was entered (and the origin is EVM) -- gates whether the\n   * one-click button renders at all. From useDepositAddress's `canDeposit`. */\n  canDeposit: boolean;\n  /** False when the host disabled one-click outright: the \"enter an amount for\n   * a one-click deposit\" helper line then never mentions it. Default true. */\n  oneClickEnabled?: boolean;\n}\n\n/** Holds exactly the QR + address card footprint while either config or a\n * deposit address is loading. It deliberately reuses `.rd-deposit-head`, so\n * the same side-by-side/wrapped layout decision applies before and after data\n * arrives and the modal never changes height at that boundary. */\nexport function DepositAddressSkeleton() {\n  return (\n    <div className=\"rd-deposit-head\" data-testid=\"rd-address-skeleton\" aria-hidden=\"true\">\n      <span className=\"rd-skeleton rd-skeleton-qr\" />\n      <span className=\"rd-skeleton rd-skeleton-address\" />\n    </div>\n  );\n}\n\n/** EIP-681 payment URI so compatible EVM wallets can prefill the transfer from a QR scan.\n * `amountWei` is optional -- omitted entirely for an amount-less \"send any amount\" deposit. */\nfunction buildPaymentUri(params: {\n  depositAddress: string;\n  originChainId: number;\n  originCurrency: string;\n  amountWei?: bigint;\n  isNative: boolean;\n}): string {\n  const { depositAddress, originChainId, originCurrency, amountWei, isNative } = params;\n  if (isNative) {\n    return amountWei !== undefined\n      ? `ethereum:${depositAddress}@${originChainId}?value=${amountWei.toString()}`\n      : `ethereum:${depositAddress}@${originChainId}`;\n  }\n  const query = new URLSearchParams({ address: depositAddress });\n  if (amountWei !== undefined) query.set(\"uint256\", amountWei.toString());\n  return `ethereum:${originCurrency}@${originChainId}/transfer?${query.toString()}`;\n}\n\n/**\n * Solana Pay URI so a Solana wallet (Phantom, ...) can prefill the SPL-token\n * transfer from a QR scan: solana:<recipient>?amount=<decimal>&spl-token=<mint>.\n * amount is a decimal UI amount (not smallest units).\n */\nfunction buildSolanaPayUri(params: { depositAddress: string; splMint: string; humanAmount: string }): string {\n  const { depositAddress, splMint, humanAmount } = params;\n  const q = new URLSearchParams();\n  if (humanAmount) q.set(\"amount\", humanAmount);\n  q.set(\"spl-token\", splMint);\n  return `solana:${depositAddress}?${q.toString()}`;\n}\n\nexport function DepositStepView({\n  depositAddress,\n  originChainId,\n  originChainName,\n  originCurrency,\n  amount,\n  tokenSymbol,\n  tokenDecimals,\n  kind = \"evm\",\n  depositState,\n  onDeposit,\n  canDeposit,\n  oneClickEnabled = true,\n}: DepositStepViewProps) {\n  const [copied, setCopied] = useState(false);\n  const [compactView, setCompactView] = useState<\"address\" | \"qr\">(\"address\");\n  const addressTooltipId = useId();\n\n  // A newly issued address always returns the compact/mobile presentation to\n  // its safe default: readable address + copy action, with QR opt-in.\n  useEffect(() => {\n    setCompactView(\"address\");\n  }, [depositAddress]);\n\n  const isNative = originCurrency.toLowerCase() === zeroAddress;\n  const isSvm = kind === \"svm\";\n\n  async function handleCopy() {\n    try {\n      await navigator.clipboard.writeText(depositAddress);\n      setCopied(true);\n      setTimeout(() => setCopied(false), 1500);\n    } catch {\n      // Clipboard API unavailable (insecure context, permissions, ...) --\n      // the address is still selectable/visible as plain text.\n    }\n  }\n\n  // Exact human amount to send (e.g. \"6.9\"), only when an amount was\n  // actually entered -- this is a reusable address, so \"no amount\" is a\n  // perfectly valid, common state (send-any-amount).\n  let amountWei: bigint | undefined;\n  if (amount) {\n    try {\n      const parsed = BigInt(amount);\n      if (parsed > 0n) amountWei = parsed;\n    } catch {\n      /* malformed -- treat as no amount */\n    }\n  }\n  const hasAmount = amountWei !== undefined;\n  let humanAmount: string | undefined;\n  if (hasAmount) {\n    try {\n      humanAmount = formatUnits(amountWei as bigint, tokenDecimals);\n    } catch {\n      humanAmount = amount;\n    }\n  }\n\n  const paymentUri = isSvm\n    ? buildSolanaPayUri({ depositAddress, splMint: originCurrency, humanAmount: humanAmount ?? \"\" })\n    : buildPaymentUri({ depositAddress, originChainId, originCurrency, amountWei, isNative });\n\n  const { isSubmitting, isConfirming, isConfirmed, needsSwitch, txHash, reverted, error } = depositState;\n\n  return (\n    <section className=\"rd-stack\">\n      {/* The QR and the address are ONE row (2026-07-31 user revision): they are\n          two encodings of the same value, and stacking them pushed everything the\n          user might want to change (origin, delivery token) a full QR height down\n          the column. QR left at its natural 172px frame, the chip + Copy right in\n          a flexible column that wraps the address across lines as needed; the row\n          On a narrow drawer the address/copy card becomes the default pane and\n          its QR icon switches to the QR pane (see `.rd-deposit-head`), avoiding\n          the old full-height vertical stack.\n\n          Center badge: the ORIGIN CHAIN's logo overlaid on the QR, so two QRs for\n          different chains are tellable apart at a glance (a wrong-chain scan is\n          this screen's most expensive mistake). Error correction is raised to \"H\"\n          (~30% recoverable) -- the 36px badge covers ~6% of the 144px code, so\n          scanning is unaffected. */}\n      <div className=\"rd-deposit-head\" data-compact-view={compactView}>\n        <div className=\"rd-deposit-qr-pane\">\n          <div className=\"rd-deposit-qr\">\n            <QRCodeSVG value={paymentUri} size={144} level=\"H\" />\n            <span className=\"rd-deposit-qr-badge\" aria-hidden=\"true\">\n              <ChainIcon chainId={originChainId} chainName={originChainName} size={28} />\n            </span>\n          </div>\n          <button\n            type=\"button\"\n            className=\"rd-mobile-address-toggle\"\n            aria-label=\"Show address and copy\"\n            onClick={() => setCompactView(\"address\")}\n          >\n            <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n              <rect x=\"8\" y=\"8\" width=\"11\" height=\"11\" rx=\"2.25\" stroke=\"currentColor\" strokeWidth=\"1.8\" />\n              <path d=\"M16 8V6.25A2.25 2.25 0 0 0 13.75 4h-7.5A2.25 2.25 0 0 0 4 6.25v7.5A2.25 2.25 0 0 0 6.25 16H8\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" />\n            </svg>\n          </button>\n        </div>\n\n        {/* Rendered IN FULL (2026-07-30 follow-up): this is the value a user\n            verifies character-by-character against their exchange/wallet, so it\n            wraps across lines rather than abbreviating. `title` is kept even\n            though it now duplicates the text -- it costs nothing and hosts/AT\n            already read the address off it. */}\n        <div className=\"rd-deposit-address\">\n          <span className=\"rd-deposit-address-label\">\n            <button\n              type=\"button\"\n              className=\"rd-address-term\"\n              aria-describedby={addressTooltipId}\n            >\n              Deposit address\n              <span id={addressTooltipId} className=\"rd-address-tooltip\" role=\"tooltip\">\n                This is a reusable, open deposit address. A wrong token or wrong chain will not be processed\n                automatically, and recovery has weaker guarantees than a one-time order address and may require a\n                manual request.\n              </span>\n            </button>\n          </span>\n          <button\n            type=\"button\"\n            className=\"rd-mobile-qr-toggle\"\n            aria-label=\"Show deposit QR code\"\n            aria-pressed={compactView === \"qr\"}\n            onClick={() => setCompactView(\"qr\")}\n          >\n            <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n              <path\n                d=\"M7.913 10.833c.69 0 1.25.56 1.25 1.25v4.163c0 .69-.56 1.25-1.25 1.25H3.75c-.69 0-1.25-.56-1.25-1.25v-4.163c0-.69.56-1.25 1.25-1.25h4.163Zm6.255 6.663h-1.665V15.83h1.665v1.666Zm3.332 0h-1.666V15.83H17.5v1.666Zm-13.334-5v3.334h3.331v-3.333H4.166Zm8.337 3.334h-1.666v-1.666h1.666v1.666Zm3.331 0h-1.666v-1.666h1.666v1.666Zm-1.666-1.666h-1.665v-1.667h1.665v1.667Zm3.332 0h-1.666v-1.667H17.5v1.667Zm-4.997-1.667h-1.666v-1.664h1.666v1.664Zm3.331 0h-1.666v-1.664h1.666v1.664ZM7.913 2.504c.69 0 1.25.56 1.25 1.25v4.163c0 .69-.56 1.25-1.25 1.25H3.75c-.69 0-1.25-.56-1.25-1.25V3.754c0-.69.56-1.25 1.25-1.25h4.163Zm8.329 0c.69 0 1.25.56 1.25 1.25v4.163c0 .69-.56 1.25-1.25 1.25h-4.163c-.69 0-1.25-.56-1.25-1.25V3.754c0-.69.56-1.25 1.25-1.25h4.163ZM4.166 4.17V7.5h3.331V4.17H4.166Zm8.329 0V7.5h3.331V4.17h-3.331Z\"\n                fill=\"currentColor\"\n              />\n            </svg>\n          </button>\n          <span className=\"rd-address-full\" title={depositAddress}>\n            {depositAddress}\n          </span>\n          <button\n            type=\"button\"\n            className=\"rd-address-copy\"\n            onClick={handleCopy}\n            aria-label={`Copy deposit address ${depositAddress}`}\n          >\n            {copied ? (\n              <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                <path\n                  d=\"m5 12 4 4L19 6\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"2.2\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                />\n              </svg>\n            ) : (\n              <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                <rect x=\"8\" y=\"8\" width=\"11\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.8\" />\n                <path\n                  d=\"M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"1.8\"\n                  strokeLinecap=\"round\"\n                />\n              </svg>\n            )}\n            <span aria-live=\"polite\">{copied ? \"Copied\" : \"Copy address\"}</span>\n          </button>\n        </div>\n      </div>\n\n      {/* EVM one-click deposit only makes sense once an amount was entered --\n          an amount-less deposit has nothing for the wallet tx to carry. SVM\n          (Solana) origins are always display-only: an EVM wallet cannot sign\n          Solana, so the user pays out-of-band from a Solana wallet. */}\n      {!isSvm && canDeposit && (\n        <>\n          <button\n            type=\"button\"\n            className=\"rd-button-primary rd-button-block\"\n            onClick={onDeposit}\n            // A REVERTED tx also reaches isConfirmed, so gating on isConfirmed\n            // alone disabled the button in exactly the state the message below\n            // tells the user to try again -- the flow could only be recovered by\n            // remounting the widget (upstream 07354b7). A revert leaves the\n            // funds untouched, so retrying is both safe and the obvious next step.\n            disabled={isSubmitting || isConfirming || (isConfirmed && !reverted)}\n          >\n            {isConfirmed && !reverted\n              ? \"Deposit sent\"\n              : isConfirming\n                ? \"Confirming...\"\n                : isSubmitting\n                  ? needsSwitch\n                    ? \"Switching network...\"\n                    : \"Confirm in wallet...\"\n                  : `One-click deposit ${tokenSymbol}${needsSwitch ? ` (switch to ${originChainName})` : \"\"}`}\n          </button>\n\n          {error && <div className=\"rd-callout rd-callout-danger\">{error}</div>}\n\n          {txHash && (\n            <p className=\"rd-card-hint\">\n              Tx: <span style={{ fontFamily: \"monospace\" }}>{txHash}</span>\n            </p>\n          )}\n\n          {isConfirmed && reverted && (\n            <div className=\"rd-callout rd-callout-danger\">\n              The deposit transaction reverted on-chain. Please check your wallet and try again.\n            </div>\n          )}\n\n          {isConfirmed && !reverted && (\n            <div className=\"rd-callout rd-callout-success\">\n              Deposit confirmed on-chain. Waiting for it to be swept...\n            </div>\n          )}\n\n          {/* (The \"or deposit manually\" divider left with the 2026-07-31 user\n              revision: the QR/address row is ABOVE this button now, so there is\n              no manual path below it to divide from.) */}\n        </>\n      )}\n\n      {isSvm && (\n        <div className=\"rd-callout rd-callout-muted\">\n          Pay from a Solana wallet (e.g. Phantom). Scan the QR or copy the address above and send{\" \"}\n          {hasAmount ? (\n            <>\n              exactly{\" \"}\n              <strong>\n                {humanAmount} {tokenSymbol}\n              </strong>\n            </>\n          ) : (\n            <>\n              any amount of <strong>{tokenSymbol}</strong>\n            </>\n          )}\n          .\n        </div>\n      )}\n\n      {/* Amount-specific only. The amount-LESS wording (\"reusable address, send\n          any amount\") moved into the checklist card, so this line is silent unless\n          there is an exact figure to name (or a one-click nudge to make). */}\n      {(hasAmount || (!isSvm && !canDeposit && oneClickEnabled)) && (\n        <p className=\"rd-card-hint\">\n          {hasAmount && (\n            <>\n              Send exactly{\" \"}\n              <strong>\n                {humanAmount} {tokenSymbol}\n              </strong>{\" \"}\n              on {originChainName} to this address.\n            </>\n          )}\n          {!isSvm && !canDeposit && oneClickEnabled && !hasAmount && \"Enter an amount to deposit in one click.\"}\n        </p>\n      )}\n\n    </section>\n  );\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Self-implemented viem-equivalent numeric utils: dapp-ui forbids viem.\n// All wei-scale math stays in BigInt end-to-end; formatUnits is a pure\n// digit-shift (floor semantics, no rounding); parseUnits THROWS on\n// over-precision input (callers treat a throw as \"invalid amount\").\n\nexport const zeroAddress = \"0x0000000000000000000000000000000000000000\" as const;\n\nconst ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;\n\nexport function isAddress(value: string): boolean {\n  return ADDRESS_RE.test(value);\n}\n\nexport function formatUnits(value: bigint, decimals: number): string {\n  const negative = value < 0n;\n  const abs = negative ? -value : value;\n  const s = abs.toString().padStart(decimals + 1, \"0\");\n  const intPart = decimals === 0 ? s : s.slice(0, -decimals);\n  const fracPart = decimals === 0 ? \"\" : s.slice(-decimals).replace(/0+$/, \"\");\n  const body = fracPart ? `${intPart}.${fracPart}` : intPart;\n  return negative ? `-${body}` : body;\n}\n\nexport function parseUnits(value: string, decimals: number): bigint {\n  const m = value.match(/^(-?)(\\d+)(?:\\.(\\d+))?$/);\n  if (!m) throw new Error(`parseUnits: invalid decimal string \"${value}\"`);\n  const [, sign, intPart, fracPart = \"\"] = m;\n  if (fracPart.length > decimals) {\n    throw new Error(`parseUnits: too many decimal places for ${decimals} decimals`);\n  }\n  const combined = BigInt(intPart + fracPart.padEnd(decimals, \"0\"));\n  return sign === \"-\" ? -combined : combined;\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/icons.tsx (v0.1.0).\n// Mechanical substitution only: the sole non-relative import\n// (`./token-logos`) is retargeted to this package's `../lib/token-logos`\n// (icons.tsx lives in components/, token-logos.ts lives in lib/) --\n// everything else, including every inline SVG path/brand color and the\n// licensing/sourcing notes below, is byte-identical to the source.\n//\n// Bundled icon registry for the widget's chain/token dropdowns. Everything\n// here is a self-contained inline SVG (or an <img> pointed at a caller-\n// supplied `logoUrl`) -- no external CDN fetch, no icon library dependency,\n// no network I/O. Keeping icons inline (rather than shipping image assets)\n// means the whole registry travels with `dist/index.js`, so a host consuming\n// the widget from `file:`/npm gets working icons with zero extra setup.\n//\n// Official-logo path data (the `d` attributes + brand colors below) for the\n// CHAIN marks -- Ethereum, BNB Smart Chain, Arbitrum, Polygon, Solana --\n// was sourced from @web3icons/react (MIT license, https://github.com/0xa3k5/web3icons),\n// a maintained icon set that vectorizes each network's official brand\n// assets. (The USDT/USDC TOKEN logos are NOT here: they ship as canonical\n// raster PNGs in token-logos.ts -- see that file's header -- rendered by\n// TokenIcon, since the simplified web3icons vectorizations didn't match the\n// coins' real brand marks.) We deliberately did NOT add web3icons as a\n// runtime dependency: the\n// package ships ~8.7k files / ~99MB unpacked (every chain/token variant),\n// which is neither tree-shakeable at *install* time nor a reasonable\n// addition to `dependencies` for the ~8 marks this widget needs -- so we\n// vetted the source, then inlined only the specific path data for the\n// chains/tokens we render. Base is the one exception: @web3icons/react\n// vectorizes Base as a rounded-square app-icon glyph rather than the brand's\n// circular logomark, so its path data is instead sourced directly from the\n// official base-org/brand-kit repo (github.com/base-org/brand-kit,\n// logo/symbol/Base_Symbol_Blue.svg, MIT license) -- see `BaseIcon` below for\n// details. See `.superpowers/sdd/official-icons-report.md` and\n// `.superpowers/sdd/base-icon-report.md` for the full source/license\n// writeup.\nimport { useId, type ReactNode } from \"react\";\nimport { bundledTokenLogo } from \"../lib/token-logos\";\n\n/** Uppercase-first monogram (1-2 chars) used as a graceful fallback when we\n * don't have a bundled brand icon for a given chain/token. */\nfunction monogram(label: string): string {\n  const trimmed = label.trim();\n  if (!trimmed) return \"?\";\n  return trimmed.slice(0, 2).toUpperCase();\n}\n\ninterface BadgeProps {\n  size: number;\n  bg: string;\n  fg?: string;\n  children: ReactNode;\n}\n\n/** Shared circular-badge shell: a colored circle clipped to `size`, with\n * `children` (glyph/monogram markup) centered inside. */\nfunction Badge({ size, bg, fg = \"#ffffff\", children }: BadgeProps) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      aria-hidden=\"true\"\n      focusable=\"false\"\n      style={{ display: \"block\", flex: \"none\", borderRadius: \"50%\" }}\n    >\n      <circle cx=\"12\" cy=\"12\" r=\"12\" fill={bg} />\n      <g fill={fg} stroke={fg}>\n        {children}\n      </g>\n    </svg>\n  );\n}\n\nfunction MonogramBadge({\n  size,\n  bg,\n  fg = \"#ffffff\",\n  text,\n}: {\n  size: number;\n  bg: string;\n  fg?: string;\n  text: string;\n}) {\n  return (\n    <Badge size={size} bg={bg} fg={fg}>\n      <text\n        x=\"12\"\n        y=\"12\"\n        textAnchor=\"middle\"\n        dominantBaseline=\"central\"\n        fontSize=\"10\"\n        fontWeight={700}\n        fontFamily=\"inherit\"\n        stroke=\"none\"\n        fill={fg}\n      >\n        {text}\n      </text>\n    </Badge>\n  );\n}\n\n/** Shared shell for the official brand SVGs below: a square canvas\n * (matching each source mark's native coordinate space -- 24x24 by default,\n * overridable via `viewBox` for marks authored in a different coordinate\n * space) that gets clipped to a circle by the same CSS technique `Badge`\n * uses -- the artwork fills the full square, and `borderRadius: 50%` on the\n * rendered element clips the corners, leaving the mark's own inscribed-\n * circle silhouette. */\nfunction OfficialIcon({\n  size,\n  viewBox = \"0 0 24 24\",\n  children,\n}: {\n  size: number;\n  viewBox?: string;\n  children: ReactNode;\n}) {\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox={viewBox}\n      aria-hidden=\"true\"\n      focusable=\"false\"\n      style={{ display: \"block\", flex: \"none\", borderRadius: \"50%\" }}\n    >\n      {children}\n    </svg>\n  );\n}\n\nfunction EthereumIcon({ size }: { size: number }) {\n  const clip = useId();\n  return (\n    <OfficialIcon size={size}>\n      <g clipPath={`url(#${clip})`}>\n        <path fill=\"#000\" d=\"M24 0H0v24h24z\" />\n        <path fill=\"#8FFCF3\" d=\"M12 4v5.912l5 2.237z\" />\n        <path fill=\"#CABCF8\" d=\"m12 4-5 8.148 5-2.235z\" />\n        <path fill=\"#CBA7F5\" d=\"M12 15.98V20l5-6.92z\" />\n        <path fill=\"#74A0F3\" d=\"M12 20v-4.02l-5-2.9z\" />\n        <path fill=\"#CBA7F5\" d=\"m12 15.049 5-2.9-5-2.236z\" />\n        <path fill=\"#74A0F3\" d=\"m7 12.149 5 2.9V9.913z\" />\n        <path\n          fill=\"#202699\"\n          fillRule=\"evenodd\"\n          clipRule=\"evenodd\"\n          d=\"m12 15.048-5-2.9L12 4l5 8.148zm-4.67-3.136 4.588-7.475v5.435zm-.068.204 4.656-2.068v4.768zm4.816-2.068v4.768l4.653-2.7zm0-.176 4.588 2.04-4.588-7.475z\"\n        />\n        <path\n          fill=\"#202699\"\n          fillRule=\"evenodd\"\n          clipRule=\"evenodd\"\n          d=\"m12 15.917-5-2.84L12 20l5-6.924zm-4.44-2.341 4.36 2.48v3.56zm4.519 2.48v3.56l4.36-6.04z\"\n        />\n      </g>\n      <defs>\n        <clipPath id={clip}>\n          <path fill=\"#fff\" d=\"M0 0h24v24H0z\" />\n        </clipPath>\n      </defs>\n    </OfficialIcon>\n  );\n}\n\nfunction BnbChainIcon({ size }: { size: number }) {\n  const clip = useId();\n  return (\n    <OfficialIcon size={size}>\n      <g clipPath={`url(#${clip})`}>\n        <path fill=\"#F0B90B\" d=\"M24 0H0v24h24z\" />\n        <path\n          fill=\"#fff\"\n          d=\"M7.635 6.449 12 4l4.364 2.449-1.6.907L12 5.809 9.24 7.356zm8.73 3.093-1.6-.906L12 10.182 9.24 8.636l-1.605.906v1.81l2.756 1.546v3.089l1.609.906 1.604-.906v-3.09l2.76-1.546zm0 4.903V12.63l-1.6.907v1.809zm1.141.64-2.76 1.542v1.813l4.365-2.453v-4.898l-1.605.902zm-1.604-7.09 1.6.907v1.81l1.609-.903V7.996l-1.605-.907L15.902 8zm-5.511 9.29v1.808L12 20l1.604-.907V17.29l-1.604.9-1.605-.907zM7.635 14.44l1.6.907v-1.81l-1.6-.906v1.813zm2.756-6.444L12 8.902l1.604-.906L12 7.089 10.395 8zm-3.898.906 1.605-.906-1.6-.907L4.888 8v1.813l1.605.903zm0 3.09-1.604-.903v4.898l4.364 2.453v-1.818L6.498 15.08v-3.093z\"\n        />\n      </g>\n      <defs>\n        <clipPath id={clip}>\n          <path fill=\"#fff\" d=\"M0 0h24v24H0z\" />\n        </clipPath>\n      </defs>\n    </OfficialIcon>\n  );\n}\n\nfunction BaseIcon({ size }: { size: number }) {\n  const clip = useId();\n  return (\n    // The upstream @web3icons/react \"network\" mark (used for the other\n    // chains above) draws Base as a rounded-square app-icon glyph, not the\n    // brand's actual logomark -- clipped to a circle it leaves a white\n    // squircle floating on a blue disc. The real Base logomark (base.org\n    // brand kit, github.com/base-org/brand-kit logo/symbol/Base_Symbol_Blue.svg)\n    // is authored in its own 111x111 space as a single path: a solid disc\n    // with a flat horizontal notch cut through it, open on the left. The\n    // path's own fill leaves that notch transparent -- a white square is\n    // layered underneath so the notch reads as the brand's white slot\n    // regardless of what the icon is rendered against, matching every other\n    // sibling mark here (each of which paints its own opaque background\n    // square before layering the glyph). It's already an inscribed circle\n    // in its own square, so it's passed through as-is (viewBox scaled to\n    // match; the source's blue is already the official `#0052FF`) rather\n    // than forced into the 24x24 space the other marks use.\n    <OfficialIcon size={size} viewBox=\"0 0 111 111\">\n      <g clipPath={`url(#${clip})`}>\n        <path fill=\"#fff\" d=\"M0 0h111v111H0z\" />\n        <path\n          fill=\"#0052FF\"\n          d=\"M54.921 110.034C85.359 110.034 110.034 85.402 110.034 55.017C110.034 24.6319 85.359 0 54.921 0C26.0432 0 2.35281 22.1714 0 50.3923H72.8467V59.6416H0C2.35281 87.8625 26.0432 110.034 54.921 110.034Z\"\n        />\n      </g>\n      <defs>\n        <clipPath id={clip}>\n          <path fill=\"#fff\" d=\"M0 0h111v111H0z\" />\n        </clipPath>\n      </defs>\n    </OfficialIcon>\n  );\n}\n\nfunction ArbitrumIcon({ size }: { size: number }) {\n  const clip = useId();\n  return (\n    <OfficialIcon size={size}>\n      <g clipPath={`url(#${clip})`}>\n        <path fill=\"#213147\" d=\"M24 0H0v24h24z\" />\n        <path\n          fill=\"#fff\"\n          d=\"m13.203 13.216-.787 2.124a.27.27 0 0 0 0 .183l1.354 3.655 1.565-.89-1.879-5.072c-.042-.117-.21-.117-.253 0m1.577-3.573a.135.135 0 0 0-.253 0l-.787 2.124a.27.27 0 0 0 0 .183l2.217 5.985 1.565-.89z\"\n        />\n        <path\n          fill=\"#fff\"\n          d=\"M11.999 4.991a.24.24 0 0 1 .111.03l5.969 3.393a.22.22 0 0 1 .112.19v6.787a.22.22 0 0 1-.112.19l-5.969 3.395a.2.2 0 0 1-.111.029.24.24 0 0 1-.113-.03l-5.968-3.39a.22.22 0 0 1-.112-.19v-6.79a.22.22 0 0 1 .112-.19l5.969-3.393a.23.23 0 0 1 .111-.03m0-.991c-.213 0-.426.054-.616.163L5.416 7.556a1.21 1.21 0 0 0-.616 1.05v6.787c0 .433.234.834.616 1.05l5.968 3.394a1.25 1.25 0 0 0 1.232 0l5.968-3.394a1.21 1.21 0 0 0 .616-1.05V8.606a1.21 1.21 0 0 0-.616-1.05l-5.97-3.393A1.24 1.24 0 0 0 11.998 4\"\n        />\n        <path\n          fill=\"#fff\"\n          d=\"m8.052 17.943.55-1.482 1.105.905-1.034.93zm3.445-9.823H9.984a.27.27 0 0 0-.254.175l-3.243 8.757 1.565.89L11.623 8.3a.132.132 0 0 0-.127-.179\"\n        />\n        <path\n          fill=\"#fff\"\n          d=\"M14.144 8.12h-1.513a.27.27 0 0 0-.253.175l-3.704 10 1.565.89 4.032-10.886a.133.133 0 0 0-.127-.179\"\n        />\n      </g>\n      <defs>\n        <clipPath id={clip}>\n          <path fill=\"#fff\" d=\"M0 0h24v24H0z\" />\n        </clipPath>\n      </defs>\n    </OfficialIcon>\n  );\n}\n\nfunction PolygonIcon({ size }: { size: number }) {\n  const clip = useId();\n  const gradient = useId();\n  return (\n    <OfficialIcon size={size}>\n      <g clipPath={`url(#${clip})`}>\n        <path fill={`url(#${gradient})`} d=\"M24 0H0v24h24z\" />\n        <path\n          fill=\"#fff\"\n          d=\"m15.88 14.86 3.794-2.165a.64.64 0 0 0 .326-.558v-4.33a.64.64 0 0 0-.326-.556L15.88 5.086a.66.66 0 0 0-.65 0L11.432 7.25a.64.64 0 0 0-.325.557v7.737l-2.662 1.517-2.661-1.517v-3.036l2.661-1.517 1.755 1.001V9.958l-1.43-.816a.66.66 0 0 0-.65 0l-3.796 2.165a.64.64 0 0 0-.325.557v4.33c0 .229.124.442.325.557l3.796 2.165c.2.114.45.114.65 0l3.796-2.165a.64.64 0 0 0 .325-.557V8.455l.048-.026 2.613-1.49 2.661 1.516v3.036l-2.661 1.517-1.753-.999v2.037l1.427.814a.66.66 0 0 0 .651 0z\"\n        />\n      </g>\n      <defs>\n        <linearGradient\n          id={gradient}\n          x1=\"3.948\"\n          x2=\"19.217\"\n          y1=\"16.617\"\n          y2=\"7.645\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"#A726C1\" />\n          <stop offset=\".88\" stopColor=\"#803BDF\" />\n          <stop offset=\"1\" stopColor=\"#7B3FE4\" />\n        </linearGradient>\n        <clipPath id={clip}>\n          <path fill=\"#fff\" d=\"M0 0h24v24H0z\" />\n        </clipPath>\n      </defs>\n    </OfficialIcon>\n  );\n}\n\nfunction SolanaIcon({ size }: { size: number }) {\n  const clip = useId();\n  const gradient = useId();\n  return (\n    <OfficialIcon size={size}>\n      <g clipPath={`url(#${clip})`}>\n        <path fill={`url(#${gradient})`} d=\"M24 0H0v24h24z\" />\n        <path\n          fill=\"#fff\"\n          d=\"M17.7 8.358a.55.55 0 0 1-.365.145H4.515c-.455 0-.684-.52-.37-.825l2.106-2.03a.54.54 0 0 1 .364-.15h12.87c.46 0 .685.524.365.83zm0 10.005c-.1.09-.23.14-.365.14H4.515c-.455 0-.684-.516-.37-.82l2.106-2.036a.53.53 0 0 1 .364-.145h12.87c.46 0 .685.52.365.825zm0-7.72a.55.55 0 0 0-.365-.14H4.515c-.455 0-.684.515-.37.82l2.106 2.035c.1.091.23.143.364.144h12.87c.46 0 .685-.52.365-.824z\"\n        />\n      </g>\n      <defs>\n        <linearGradient\n          id={gradient}\n          x1=\"4.001\"\n          x2=\"20.408\"\n          y1=\"50.258\"\n          y2=\"50.108\"\n          gradientUnits=\"userSpaceOnUse\"\n        >\n          <stop stopColor=\"#599DB0\" />\n          <stop offset=\"1\" stopColor=\"#47F8C3\" />\n        </linearGradient>\n        <clipPath id={clip}>\n          <path fill=\"#fff\" d=\"M0 0h24v24H0z\" />\n        </clipPath>\n      </defs>\n    </OfficialIcon>\n  );\n}\n\nexport interface ChainIconProps {\n  chainId: number;\n  /** Display name, used only for the unknown-chain fallback monogram. */\n  chainName?: string;\n  size?: number;\n}\n\n/** Official-logo circular badge for a chainId (see the module-level comment\n * for sourcing/licensing). Falls back to a neutral monogram (from\n * `chainName`'s initial) for chains we don't have a bundled official icon\n * for, e.g. custom/testnet chains such as CROSS (612055). */\nexport function ChainIcon({ chainId, chainName, size = 20 }: ChainIconProps) {\n  switch (chainId) {\n    case 1: // Ethereum mainnet\n      return <EthereumIcon size={size} />;\n    case 56: // BNB Smart Chain\n      return <BnbChainIcon size={size} />;\n    case 8453: // Base\n      return <BaseIcon size={size} />;\n    case 42161: // Arbitrum One\n      return <ArbitrumIcon size={size} />;\n    case 137: // Polygon\n      return <PolygonIcon size={size} />;\n    case 792703809: // Solana\n      return <SolanaIcon size={size} />;\n    case 612055: // CROSS -- custom chain, no official public logo to bundle\n      return <MonogramBadge size={size} bg=\"#0b1f3a\" text=\"CR\" />;\n    default:\n      return (\n        <MonogramBadge\n          size={size}\n          bg=\"#676b7a\"\n          text={monogram(chainName ?? String(chainId))}\n        />\n      );\n  }\n}\n\nexport interface TokenIconProps {\n  symbol: string;\n  /** When provided (non-empty), rendered as an <img> instead of a bundled\n   * brand SVG -- lets API-supplied logos take priority. */\n  logoUrl?: string;\n  size?: number;\n}\n\n/** Official-logo circular badge for a token symbol. Prefers `logoUrl`\n * (rendered as an <img>) when supplied; otherwise a bundled official brand\n * logo (see token-logos.ts) for the tokens we ship (ONEUSD, pONEUSD, USDT,\n * USDC), or a neutral symbol-initial monogram for custom tokens (e.g. USDTx)\n * that have no official logo to bundle. */\nexport function TokenIcon({ symbol, logoUrl, size = 20 }: TokenIconProps) {\n  // API-supplied logo wins; otherwise fall back to a bundled brand logo\n  // (base64 data URI in token-logos.ts) for the tokens we ship. Rendered as\n  // an <img> so each SVG's internal ids are isolated per element (no\n  // duplicate-id collisions across trigger + option) and raster logos work.\n  const resolvedLogo =\n    logoUrl && logoUrl.trim().length > 0 ? logoUrl.trim() : bundledTokenLogo(symbol);\n  if (resolvedLogo) {\n    return (\n      <img\n        src={resolvedLogo}\n        alt=\"\"\n        aria-hidden=\"true\"\n        width={size}\n        height={size}\n        style={{ display: \"block\", flex: \"none\", borderRadius: \"50%\", objectFit: \"cover\" }}\n      />\n    );\n  }\n\n  return <MonogramBadge size={size} bg=\"#676b7a\" text={monogram(symbol)} />;\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Bundled token brand logos, embedded as base64 data URIs (SVG or PNG) so the\n// widget ships them in-source (no external CDN fetch, no network I/O) -- the\n// same zero-setup philosophy as the inline icons in icons.tsx. Rendered via an\n// <img> (see TokenIcon), which isolates each SVG's internal ids (clip/mask/\n// gradient) per element, avoiding the duplicate-id collisions that inlining\n// the same SVG twice on one page would cause.\n//\n// USDT/USDC are instead raster PNGs (data:image/png) from the Trust Wallet\n// assets registry -- the de-facto canonical, contract-address-keyed source\n// that block explorers and wallets share for official token artwork -- so the\n// dropdown shows each stablecoin's real brand logo rather than a simplified\n// vectorization.\n//\n// Sources (downloaded, not referenced at runtime):\n//   ONEUSD  -- https://contents.crosstoken.io/wallet/token/images/ONEUSD.svg\n//   pONEUSD -- https://www.punch.win/images/usd-icon.svg\n//   USDT    -- trustwallet/assets ethereum 0xdAC17F958D2ee523a2206206994597C13D831ec7/logo.png\n//   USDC    -- trustwallet/assets ethereum 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png\n\nexport const ONEUSD_LOGO = 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNTQwIDU0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzc0MDAwXzM4NTkwKSI+CjxtYXNrIGlkPSJtYXNrMF83NDAwMF8zODU5MCIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSI1NDAiIGhlaWdodD0iNTQwIj4KPHBhdGggZD0iTTI3MCAwQzU2Ljg1NjMgMCAwIDU2Ljg1NjMgMCAyNzBDMCA0ODMuMTQ0IDU2Ljg1NjMgNTQwIDI3MCA1NDBDNDgzLjE0NCA1NDAgNTQwIDQ4My4xNDQgNTQwIDI3MEM1NDAgNTYuODU2MyA0ODMuMTQ0IDAgMjcwIDBaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazBfNzQwMDBfMzg1OTApIj4KPHBhdGggZD0iTTUzOS43MzEgMC4yNzA1MDhIMFY1NDAuMDAxSDUzOS43MzFWMC4yNzA1MDhaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNzQwMDBfMzg1OTApIi8+CjxwYXRoIGQ9Ik0xODguNjIzIDU0MC4wMDFINTM5Ljg2NVYxMDIuOTM2TDE4OC42MjMgMzI3LjkzNlY1NDAuMDAxWiIgZmlsbD0idXJsKCNwYWludDFfbGluZWFyXzc0MDAwXzM4NTkwKSIvPgo8cGF0aCBkPSJNMTg4LjYyMyA1NDAuMDAySDBWMTI4LjUzNUwxODguNjIzIDMyNy45MzZWNTQwLjAwMloiIGZpbGw9InVybCgjcGFpbnQyX2xpbmVhcl83NDAwMF8zODU5MCkiLz4KPC9nPgo8cGF0aCBkPSJNMTQ3Ljg4NCA0MDMuNDkzQzEzOC4wMjUgNDAzLjQ5MyAxMjkuMjk2IDQwMS40MzkgMTIxLjY5NiAzOTcuMzMxQzExNC4wOTYgMzkzLjEyIDEwOC4xNCAzODcuMzY5IDEwMy44MjYgMzgwLjA3N0M5OS42MTU1IDM3Mi43ODYgOTcuNTEwMiAzNjQuNTcgOTcuNTEwMiAzNTUuNDI5VjI5OC44NjZDOTcuNTEwMiAyOTYuNDk5IDk5LjQyOSAyOTQuNTggMTAxLjc5NiAyOTQuNThIMTEzLjQwNUMxMTUuNzcyIDI5NC41OCAxMTcuNjkxIDI5Ni40OTkgMTE3LjY5MSAyOTguODY2VjM1NC41MDVDMTE3LjY5MSAzNjAuMzU5IDExOS4wMjYgMzY1LjU5NyAxMjEuNjk2IDM3MC4yMThDMTI0LjM2NiAzNzQuNzM3IDEyOC4wMTIgMzc4LjMzMSAxMzIuNjM0IDM4MS4wMDJDMTM3LjI1NSAzODMuNTY5IDE0Mi4zMzkgMzg0Ljg1MyAxNDcuODg0IDM4NC44NTNDMTUzLjg0MSAzODQuODUzIDE1OS4xODEgMzgzLjU2OSAxNjMuOTA2IDM4MS4wMDJDMTY4LjYzIDM3OC4zMzEgMTcyLjMyNyAzNzQuNzM3IDE3NC45OTcgMzcwLjIxOEMxNzcuNjY3IDM2NS41OTcgMTc5LjAwMiAzNjAuMzU5IDE3OS4wMDIgMzU0LjUwNVYyOTguODY2QzE3OS4wMDIgMjk2LjQ5OSAxODAuOTIxIDI5NC41OCAxODMuMjg4IDI5NC41OEgxOTQuMTI3QzE5Ni40OTQgMjk0LjU4IDE5OC40MTMgMjk2LjQ5OSAxOTguNDEzIDI5OC44NjZWMzU1LjQyOUMxOTguNDEzIDM2NC41NyAxOTYuMjU2IDM3Mi43ODYgMTkxLjk0MyAzODAuMDc3QzE4Ny43MzIgMzg3LjM2OSAxODEuODI3IDM5My4xMiAxNzQuMjI3IDM5Ny4zMzFDMTY2LjczIDQwMS40MzkgMTU3Ljk0OSA0MDMuNDkzIDE0Ny44ODQgNDAzLjQ5M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yNjcuNjU3IDQwNC4xMDlDMjYwLjM2NSA0MDQuMTA5IDI1My42MzggNDAzLjMzOSAyNDcuNDc2IDQwMS43OThDMjQxLjQxNyA0MDAuMTU1IDIzNS44NzEgMzk3Ljg0NCAyMzAuODM5IDM5NC44NjZDMjI2Ljg3MiAzOTIuNDY5IDIyMy4yMDQgMzg5LjU3NCAyMTkuODM2IDM4Ni4xNzlDMjE4LjM5OCAzODQuNzMxIDIxOC4zMjEgMzgyLjQ0OSAyMTkuNTUgMzgwLjgyMUwyMjYuMTg2IDM3Mi4wM0MyMjcuODAzIDM2OS44ODggMjMwLjk2MyAzNjkuNzUxIDIzMi44ODMgMzcxLjYyNkMyMzcuOTUzIDM3Ni41NzYgMjQzLjAyMyAzODAuMjE1IDI0OC4wOTIgMzgyLjU0MkMyNTQuNDYgMzg1LjMxNSAyNjEuMjg5IDM4Ni43MDIgMjY4LjU4MSAzODYuNzAyQzI3Mi43OTIgMzg2LjcwMiAyNzYuNzQ2IDM4Ni4xODggMjgwLjQ0MyAzODUuMTYxQzI4NC4yNDMgMzg0LjAzMSAyODcuMzI0IDM4Mi40MzkgMjg5LjY4NiAzODAuMzg1QzI5Mi4xNTEgMzc4LjIyOSAyOTMuMzgzIDM3NS42MSAyOTMuMzgzIDM3Mi41MjlDMjkzLjM4MyAzNzAuNDc1IDI5Mi43NjcgMzY4LjcyOSAyOTEuNTM0IDM2Ny4yOTFDMjkwLjQwNSAzNjUuNzUxIDI4OC44MTMgMzY0LjQ2NyAyODYuNzU5IDM2My40NEMyODQuODA4IDM2Mi4zMSAyODIuNTQ4IDM2MS4zODYgMjc5Ljk4MSAzNjAuNjY3QzI3Ny40MTMgMzU5Ljk0OCAyNzQuNzQzIDM1OS4zMzIgMjcxLjk3IDM1OC44MTlDMjY5LjE5NyAzNTguMzA1IDI2Ni40MjQgMzU3Ljg0MyAyNjMuNjUxIDM1Ny40MzJDMjU3LjE4MSAzNTYuNDA1IDI1MS40MyAzNTUuMDcgMjQ2LjM5OCAzNTMuNDI3QzI0MS4zNjUgMzUxLjY4MSAyMzcuMTAzIDM0OS40NzMgMjMzLjYxMiAzNDYuODAzQzIzMC4xMiAzNDQuMTMyIDIyNy40NSAzNDEgMjI1LjYwMSAzMzcuNDA2QzIyMy44NTUgMzMzLjgxMSAyMjIuOTgyIDMyOS42NTIgMjIyLjk4MiAzMjQuOTI4QzIyMi45ODIgMzE5Ljk5OCAyMjQuMTEyIDMxNS41ODIgMjI2LjM3MSAzMTEuNjc5QzIyOC42MzEgMzA3LjY3NCAyMzEuODE0IDMwNC4yODUgMjM1LjkyMiAzMDEuNTEyQzI0MC4wMyAyOTguNzM5IDI0NC43NTUgMjk2LjYzNCAyNTAuMDk1IDI5NS4xOTZDMjU1LjUzOCAyOTMuNjU2IDI2MS40NDMgMjkyLjg4NSAyNjcuODExIDI5Mi44ODVDMjc0LjU4OSAyOTIuODg1IDI4MC43IDI5My42MDQgMjg2LjE0MyAyOTUuMDQyQzI5MS42ODggMjk2LjQ4IDI5Ni41NjcgMjk4LjU4NSAzMDAuNzc3IDMwMS4zNThDMzAzLjkzOCAzMDMuMzYzIDMwNi43MjMgMzA1LjY4NiAzMDkuMTMyIDMwOC4zMjdDMzEwLjUyMiAzMDkuODUyIDMxMC40IDMxMi4xNjkgMzA5LjAxMSAzMTMuNjk2TDMwMi4yNDEgMzIxLjEzNUMzMDAuNDkgMzIzLjA1OCAyOTcuNDUzIDMyMi45NzMgMjk1LjU0OCAzMjEuMjAzQzI5My44MzkgMzE5LjYxNCAyOTEuOTg3IDMxOC4xODYgMjg5Ljk5NCAzMTYuOTE3QzI4Ni43MDcgMzE0Ljc2IDI4My4xNjQgMzEzLjE2OCAyNzkuMzY0IDMxMi4xNDFDMjc1LjU2NSAzMTEuMDEyIDI3MS41NTkgMzEwLjQ0NyAyNjcuMzQ5IDMxMC40NDdDMjYyLjgzIDMxMC40NDcgMjU4LjgyNCAzMTAuOTYgMjU1LjMzMyAzMTEuOTg3QzI1MS44NDEgMzEzLjAxNCAyNDkuMDY4IDMxNC41MDQgMjQ3LjAxNCAzMTYuNDU1QzI0NC45NiAzMTguNDA2IDI0My45MzMgMzIwLjc2OCAyNDMuOTMzIDMyMy41NDFDMjQzLjkzMyAzMjUuODAxIDI0NC42MDEgMzI3LjgwMyAyNDUuOTM2IDMyOS41NDlDMjQ3LjI3MSAzMzEuMTkyIDI0OS4xMTkgMzMyLjYzIDI1MS40ODEgMzMzLjg2MkMyNTMuOTQ2IDMzNC45OTIgMjU2LjgyMiAzMzUuOTY4IDI2MC4xMDggMzM2Ljc4OUMyNjMuMzk1IDMzNy41MDggMjY2Ljk4OSAzMzguMTc2IDI3MC44OTIgMzM4Ljc5MkMyNzYuODQ4IDMzOS43MTYgMjgyLjQ0NSAzNDAuODk3IDI4Ny42ODMgMzQyLjMzNUMyOTIuOTIxIDM0My43NzMgMjk3LjU0MiAzNDUuNjczIDMwMS41NDggMzQ4LjAzNUMzMDUuNTUzIDM1MC4yOTQgMzA4LjYzNCAzNTMuMTcgMzEwLjc5MSAzNTYuNjYyQzMxMy4wNSAzNjAuMDUxIDMxNC4xOCAzNjQuMzEzIDMxNC4xOCAzNjkuNDQ4QzMxNC4xOCAzNzYuNTM0IDMxMi4yMjggMzgyLjY5NiAzMDguMzI2IDM4Ny45MzRDMzA0LjQyMyAzOTMuMDY5IDI5OC45OCAzOTcuMDc0IDI5MS45OTYgMzk5Ljk1QzI4NS4xMTYgNDAyLjcyMyAyNzcuMDAyIDQwNC4xMDkgMjY3LjY1NyA0MDQuMTA5WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTM0Mi43NjkgNDAyLjU2OUMzNDAuNDAyIDQwMi41NjkgMzM4LjQ4MyA0MDAuNjUgMzM4LjQ4MyAzOTguMjgzVjI5OS4wMkMzMzguNDgzIDI5Ni42NTMgMzQwLjQwMiAyOTQuNzM0IDM0Mi43NjkgMjk0LjczNEgzODUuMzM1QzM5NC4zNzIgMjk0LjczNCA0MDIuNDM0IDI5Ni4xMiA0MDkuNTIxIDI5OC44OTNDNDE2LjYwNyAzMDEuNTYzIDQyMi42MTUgMzA1LjM2MyA0MjcuNTQ0IDMxMC4yOTNDNDMyLjQ3NCAzMTUuMjIyIDQzNi4xNzEgMzIwLjk3NCA0MzguNjM2IDMyNy41NDZDNDQxLjIwMyAzMzQuMDE3IDQ0Mi40ODcgMzQxLjA1MSA0NDIuNDg3IDM0OC42NTFDNDQyLjQ4NyAzNTYuMzU0IDQ0MS4yMDMgMzYzLjQ5MSA0MzguNjM2IDM3MC4wNjRDNDM2LjE3MSAzNzYuNTM0IDQzMi40NzQgMzgyLjIzNCA0MjcuNTQ0IDM4Ny4xNjRDNDIyLjYxNSAzOTEuOTkxIDQxNi42MDcgMzk1Ljc5IDQwOS41MjEgMzk4LjU2M0M0MDIuNDM0IDQwMS4yMzQgMzk0LjM3MiA0MDIuNTY5IDM4NS4zMzUgNDAyLjU2OUgzNDIuNzY5Wk0zNTguNTEgMzg0LjY2M0MzNTguNTEgMzg1LjI2OCAzNTcuNjk4IDM4NS40NjggMzU3LjQxNiAzODQuOTMzQzM1Ny4yMTMgMzg0LjU0NyAzNTcuNDkzIDM4NC4wODMgMzU3LjkzIDM4NC4wODNIMzg0LjU2NEMzOTAuODI5IDM4NC4wODMgMzk2LjI3MiAzODMuMjEgNDAwLjg5NCAzODEuNDY0QzQwNS42MTggMzc5LjYxNSA0MDkuNTIxIDM3Ny4wOTkgNDEyLjYwMiAzNzMuOTE1QzQxNS42ODMgMzcwLjczMiA0MTcuOTQyIDM2Ny4wMzUgNDE5LjM4IDM2Mi44MjRDNDIwLjkyIDM1OC41MSA0MjEuNjkgMzUzLjc4NiA0MjEuNjkgMzQ4LjY1MUM0MjEuNjkgMzQzLjYxOSA0MjAuOTIgMzM4Ljk5NyA0MTkuMzggMzM0Ljc4N0M0MTcuOTQyIDMzMC40NzMgNDE1LjYzMSAzMjYuNzI1IDQxMi40NDcgMzIzLjU0MUM0MDkuMzY3IDMyMC4yNTUgNDA1LjUxNSAzMTcuNzM5IDQwMC44OTQgMzE1Ljk5M0MzOTYuMjcyIDMxNC4xNDQgMzkwLjgyOSAzMTMuMjIgMzg0LjU2NCAzMTMuMjJIMzU3Ljg0N0MzNTcuMjk5IDMxMy4yMiAzNTYuOTg4IDMxMi41OTEgMzU3LjMyMSAzMTIuMTU1QzM1Ny43MDYgMzExLjY1MiAzNTguNTEgMzExLjkyNCAzNTguNTEgMzEyLjU1N1YzODQuNjYzWiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTI1NS4wNiA4NS4zNzAxTDIwNS4zODkgMTE0LjA2OUMxOTYuNjI4IDExOS4xMDUgMTkzLjU5MiAxMzAuNDE5IDE5OC42MjggMTM5LjE4QzIwMy42NjQgMTQ3Ljk0MiAyMTQuOTc4IDE1MC45NzcgMjIzLjc0IDE0NS45NDFMMjY1Ljc1MyAxMjEuNjU3TDI0NS43NDcgMTQzLjExM1YyMjcuMjc3QzI0NS43NDcgMjM3LjQxOCAyNTQuMDI1IDI0NS42OTcgMjY0LjE2NiAyNDUuNjk3QzI3NC4zMDcgMjQ1LjY5NyAyODIuNTg2IDIzNy40MTggMjgyLjU4NiAyMjcuMjc3VjEwMS4zMDZDMjgyLjU4NiA5Ny43ODc4IDI4MS40ODIgOTQuMzM4NSAyODAuMTAyIDkyLjA2MTlDMjc0LjkyOCA4My4zNjk1IDI2My43NTIgODAuMjY1IDI1NC45OTEgODUuMzAxMUwyNTUuMDYgODUuMzcwMVoiIGZpbGw9IndoaXRlIi8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl83NDAwMF8zODU5MCIgeDE9Ii02Mi41MTUiIHkxPSItNjQuODA0MyIgeDI9IjU3NS40MzQiIHkyPSI1NzcuNzI2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGMzc1MkQiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRUMxMjcyIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl83NDAwMF8zODU5MCIgeDE9IjEzNC43MzEiIHkxPSIxMjQuMjIzIiB4Mj0iMzk3LjU5IiB5Mj0iMzUwLjE2NiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwLjUiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSJ3aGl0ZSIgc3RvcC1vcGFjaXR5PSIwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQyX2xpbmVhcl83NDAwMF8zODU5MCIgeDE9IjMzNi42OTIiIHkxPSIxOC43Mjk1IiB4Mj0iODguMjQ4NSIgeTI9IjM0Mi4yMTgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0id2hpdGUiIHN0b3Atb3BhY2l0eT0iMC41Ii8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0id2hpdGUiIHN0b3Atb3BhY2l0eT0iMCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzc0MDAwXzM4NTkwIj4KPHJlY3Qgd2lkdGg9IjU0MCIgaGVpZ2h0PSI1NDAiIHJ4PSIyNzAiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg=='\n\nexport const PONEUSD_LOGO = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDMwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0xNTAgMzAwQzIzMi44NDMgMzAwIDMwMCAyMzIuODQzIDMwMCAxNTBDMzAwIDY3LjE1NzMgMjMyLjg0MyAwIDE1MCAwQzY3LjE1NzMgMCAwIDY3LjE1NzMgMCAxNTBDMCAyMzIuODQzIDY3LjE1NzMgMzAwIDE1MCAzMDBaIiBmaWxsPSJibGFjayIvPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTAsMTUwKSBzY2FsZSgwLjg1KSB0cmFuc2xhdGUoLTE1MCwtMTUwKSI+CjxwYXRoIGQ9Ik0xNjYuOTkzIDM2LjMyMFY1OS41ODVIMjAwLjM3OEwyMjIuNjgwIDgxLjg4N1YxMDcuNzc1SDE5MC4xMzJWOTIuMTMySDEwOS44NjlWMTMzLjcyNkgxOTMuMzE3TDIyMi42ODAgMTYzLjA4N0wyMjIuNjgxIDIwNi42MzZWMjE3LjkzOEwyMDEuNDM2IDIzOS4xODNIMTY4Ljg1OFYyNjMuNjgwSDE0MC4yNDFWMjM5LjE4M0g5OC41NjRMNzcuMzIwIDIxNy45MzlWMTkxLjM3M0gxMDkuODY5VjIwNi42MzZIMTkwLjEzMlYxNjYuMjc0SDEwMS44NTRMNzcuMzIwIDE0MS43NDBWODEuODg3TDk5LjYyMiA1OS41ODVIMTQwLjI0MVYzNi4zMjBIMTY2Ljk5M1oiIGZpbGw9IiNGQjg1MjQiLz4KPC9nPgo8L3N2Zz4K'\n\n\nexport const USDT_LOGO = 'data:image/png;base64,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'\n\nexport const USDC_LOGO = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALUAAAC1CAYAAAAZU76pAAAkA0lEQVR42uycA7DsSBSGe23bnHRmbdu2UHw2u7O2bds2S8+YdNa2bUtn51uUnpmezPmqTvHe9I9z7whGmQT2v3Om9Ih8oXRAniQ+3zxxxUHW55l1+ZmJz2+1PtzdnJD48MLEDD/L7/C7XINrcU2uzRmcxZlGUaYWqx7/4twrD2ysUs+K7a0P3VMfLrZZeNT68GZzvm7OL82RqTxc82vO4CzO5Gw0oAVNRlEmlq2OHzxz6oqtrA9HJy5cb114xvrwK8sWyfyKJrShEa1oNooCsHLv12erZaPraVZ0ac7Dictftz78zgK1yPyOZrTjAS94Mkr7YbPG1okrjrMujGQ5qjR4whsejVJteOBlXXE6D84ov00m4BnvRqkGy/d7en7r8/2b84j14TeKbtP5jQzIgkyM0nrUfaNms3CB9eGVMQrWeYVsyMgo8ZMObKxrXX4jT4lNsFydr8mKzIwSH8nAYmfrizsnr1wdsiNDo5RPzYe1rC/umzrF6pAlmRqljP/MT6TWhWunTbE6ZEvGRpn2LH/84Nmty4+yvvhq2harQ8ZkTeZGmTbYrNiDl4inb7E6ZE72Rpl6rHr4kytbl99Vbrk6dEAXRpkyUld0tT58Fk2xOp/RiVEmndWObCxmfbgl2nJ1bqEjo0wcqc93sz5/J+5SdeiIrowyoYUOJ7RWsTp0ZpSxPu+8cOLDQ61ZrA7d0aFR/sW6xhrWF8+2drE6dEiX+jK3y3e1PnxRmWJ1vqDTNv4PHfpUs1gdum3DhS5Or3axOnRs2gXeoN4exerQtaky63Z5Yhbr88tjLiFxQVYemMtSvUfLwt1HytJ9RkenEU1oQyNa0RxzpnRO99V8d10WHo0x9DQLsny/hizUbaQs1mOULN+/IXuf/4IMvOVN2enM52TRHiOl5nJ+rlSNaEALmtCGRrSiGe14+FdjfEP3lXu3X+LCPTEu8wr9c5m703Dhv99u5zwvZzz8njz33g/y069/Cnz38x/S58bXZbGeo6TmytPK2WhAC5oAjWhFM9rxgBc8xbjc7ECVnuW4JJpFbs5KA3NZsvdoWaDrSKkfVki/m9+Qx5/9UsbF980l2vC4p2SJ3uXdFeFsNKBlXOABL3jCGx7xmka02OxC6y+0D+fHsszc9+RmesHmbHLC03Le4x/IG5/+JBPD9qdzN2RUafo5Gw0TAZ7whke84hnvMS33+aZVSbNwRCwP/rhpnq/LCNnhjOfkykEfydc//i6TwjanPsvNf2keOBsNkwAe8YpnvJNBNA8q2Y1WfNru0LKDq2dBlug1WubvOkLWP+4puXHEJ//fV4bKLzUAnvFOBmRBJvUI7nOzI6ZV4LuVrQ9/lHlXg2cK+O/Ef6YzH35fvvz+NwFot6UGIAOyIBOyqbnS72//wa6Y2LEuX8H68GGZ95tZgMV7jZIOV74qL37wowC0+VIDkAnZkBHXLfv+9ofsTNTfsv93e+cAXcmyheF+0rVt5GWubdu2bQ+ubdu2bWmYEzujjG1L9fa30pO3cnl2n97VJ0n/a/VDJjmp2v+f6qpdG1TcTMo9x0XE4uLS2vqGcvd+yVgXJ/bMA1EzhhiBjbAVNsN2ibkB0Uzedkug4n1SKzS3a5zyLxY/7uBxs1zc2OvOKrdCgqLmdzOGmIGtsBm2w4aJrdhoJx8vV85OYHXmv2Wl6e6kdYR7vcdoZ4HG0TPdhleVuNUuLUpM1PxuxsBYDIDtsCG2bGFbnw8ayqsKozKoyUlsNzjwcG1cPmiqs8LJTzbg703cW8AYGIsRsCG2xKZJbUcm50klVvcXGUzG5+RZUXhVLiXG5wZt1pz5Lm7MmrvAfVA61h3xUC0XH3nh32UMjIUxMTbGGDOwJTbFttgYW/ueZwZNtau4aFaPVeUSgYPTY98Od3Gj36gZ7oEvh7ndbq+Uywqu0rWvY/vtFmNibIyRsTLmmIFtsTG25ve2nzjsDl2KtvNNKisVMQ0flY5zcaJKAoM6vdFI4BCveS4oCAzK23BOxsYYGStjZuzMIUZgY2yNzb0LG20l5b6r9Cno5cW4G8h/d+09ycWFhuHT3RUSxsmrfdEzuxHKaUCgrV0YM2NnDsyFOcUFbL0BtvcsbLSFxjxfg2eu9EkcQfH8d6b/FBcHRk6a7W7/eLAr6Nh0u8aKxOcXtsa6G6GNmANzYU7MjTnGAWwecuBV2GjMX+/BzqXr0qTS5wq9kbizygbE4+F4/ueRbhuJg2B1W+cK66ti/wJnTsyNOTLXGIDt4cD3ij0brfkP+LffQ/MqimWF7isHqpOfaiDWmM/1ENj/R4+XcFXmypyZexwrNlx43WOjNS917nxF2GG8Da4sdmUDc1+hX+sxmhUMVxU+WNOVks/Hc7C6XJiscZk8l7Z8+Br/xvesZxzEz+czZ+aODXIEXMAJ3HiL9ENzls3n/ynlpWp9rNCQvprsEb8kIyUHTJ01Tw5P+F3xaNj7XREohHNJ8kXlePdTw0T3XW2Lh6/xb3xP06rnwa/P3LEBtsAmOQBO4AaOvKzYaA7tGYWUllzoY0+4pqxkrC4vdxvlckH98OlEtnnN21tJVt9Tn+7t5mdxH8T38L38jM98TGyCbXIA3MARXHk5k6A9i1V6MRq7+wgdXeLsbu7WDwe7XPBJ+TghsilXz+et2H/O6Oa+ULxd+F5+xudtLDbBNtgoB8ARXHkJXUV7aDDmXMNMFx9Gx+AnPtHAKhYZr3Qb7XBvLX++/0sDBMrWIkvwvfyMd982tsFG2Coi4Aiu4MzLuNFgEBc2vqrb0vKh420NTVIsCbHlbuK0uS4qnv5+hFv2/B4cyBK5RFlEBKpYAflefiaRSxtshK2wWUTAFZzBnQ97j0eLcSXQdrQ28rpyOiee4cf6idH9zz+NxLim8Qr2ovYfR4PNsF1EwBncwaGPMXeMZS8tH9bfdL+EGOSy4B4pyhIVZE1z47Vm1lWKUlHzYCtshu2wYVTAHRwW2I+5P5rMsTtW5gQPccKSxl8le7QFUQXNaiMupuYVOhW12oXKih1Z2HAHh17iztFkTkFL1rHS7OtWl1dX1AuWT8vHcVCRz4i6QqeiDoWNDbElNo16MQOXcGoec402IwYtFe1u7F4ifSjytqOo/xRuzLLYQ6eiVuyxsSm2jboNgVNzNyrajFoy7FXLa3D2cZycJ1MfTokxU+a4bW4o45YscUEXhLEc1K0LTvxJlcn+cdk498/Tujp+viBPhI1NsS02VgIu4RRura/RX42wSpevIj84xWpQlJ1dXib+RdUEp8WcufPd8Y/XE2YJCQlWTKX8b1NwFHtSVjgi2br1mZS9qGWl/sepXVkhEQKflXjkIHPDttgYWysBp3ALx5bjnIJGteUOOpkZjcPhuU1Gi4L7Ph8a3mQlV1+E62ZiH/a6q8pd+dYAxzZiwJiZjvp18xdkf+CdLiXBSiSs84Wuo9z5L/Z1O99aSaATKVvyexJLI2u+2cXWEQC3cGz6x4lGVRX/5VXYyywkU0TBoSRKOGmmcQrBOYnUqMD1hdg4CF0uyancBiLgODFBLjPeKx7rTn+md3PHgDBuJZFaKtgamysBt3AM12ZjRKNZdygo7Fi0peVemlcbq5IW1Gbe4/ZKiPae5czvZItx6av9w2Age1QMmkrAk7zK6XBAGK7fOWNj5o3Nsb0ScAzXpntrtJrlDWLmJsvVjhWgPIIL745PJIBGXvusWj4LTS4mlwr7ig/2h7qJLgm8mxnDXp23RCLbLWyO7ZWAY7iGc7PxodVsvR7VNoNgn9bdnftC3yirFq99VktvZBLMz76y85uNbjoxyAliyPhZ7tAHarEfr12vWy9sju3hQAm4ZsyW46v+85jpLiXbWA2AUz2HK202OBeNRz9ah3G8kMnvYB9LJNsDXw51+QIOoYc9WEuNad/7a2wPB3ChAVzDOdybjQ/N/kn+YcmNVobhVH/0I3VOC2KOw1JYXlbodcMUqCcolpNnmCTCPvj+GvEs+C2Dhu3hAC6UgHO4N1uQ0OyfbD1KfrDcerxXPEbpk15A5ykOSgjOS7Is5F0uaU/5iiFSqXSL60rdKhf5u0nl98ABXMCJAnBuugVBs79fcalj+ZpWhR4JSN/y+jI3dupcXdpQ11FhFSJf5Rh6UDI35zw+MEFWVUqC9R4xw/UZ2fTwtThALb3lxC6soD7b3sEFnCgA53CPBswKS6Ld34ubPsLKIIQlXvV2o9Ng7rwFVOGUPWQPP6SFcSRduRGM2FvlG0mspQTYUfLK3V1yAbe6vkxW1f8/fO1I+TcSYD8tH0+QfU7VWJvPGZ4euICTOfNUN41wjwbMxoV2PRZ7xI2XoTSW9oDI5QZ+Ug4ZXlZpDmDnv9TXRcHnleNY4XFhsd9lC9Nc94+v8d88fI1/43v4/4j+mR+Gu3mcwPRRcVzVe/MI8cAF2TLf1EzQHhjRAFrwWFTyJvdXqa9QZNXaYX/569biRFmJPKUJicEziEziNiZr44hlZe7vlhChkhUe9grPugc6t25cuR/xcK0bKm47LU56qoGqSV791mxBTpNLISXQgFmLEbSLhltmuHQuXsmqoxar092fDlE328Fpv76nPSMr5yEP1Dige60OcH8/5eewL3jU4jdNCbv8/mnKvfxbvcY0/eF7jQtpcs3CkQJowLIy1jw07K3yEn+dpQN08QPXvzsIsjwdgJp80neqbs1omzyBLUuusRnNgmTPebX8kWgwfMJsx23jOp7LDsMNHCmABtCCdSUn+6txMiF2ldiBSdOzX4G4vdv1tkqE5oMgEkZ51Em/xzxaH2sZBrYia16aUbf6OPu5Pt5sFT78PjjS3LSiAbSAJsyuzM2LPrICETfR5c1G7QrI4YqDha/DD8JU7WkbR8+QxvbFscc1UK30CokA1OCJ74Z7z56BGziCKw3QApoo9FFM0qKA+vrhzdxbRbqCKde+MxBy/YWUyv6Q5vRTFJFon1WO548hds8MLkU8IpNnZD+WZ34cIXvyrt6DneAIrhRAC2gCbdgUagdg3U49VpAvDo37lxAPvOk1pa526DRNPDGvNeJ4vZGDkA6VmIqZioZI75WMlcNh/KJmBYP0x2X1zTIcF78xq6Z3UcMRXMFZlkALaAJtWIxpKFoOwPodi3ex8HxwmNhXDK7xwZYOmOq5G1Yo6gd0on7fSNQ8fCZ7/BveH+i+l3DXHn0nu58bJrV4evWb4t4uGuMOuq8GexH7nFi3MDjLEmgBTVg5Aeah5dDzUXK8RYEa/JnnkQyg6wrFz3nN9FhFRH3EQ3VuliIfr2rwtOa9pUEiBXv1sCl+8W8F2ZMwwGpHAkGieZpwBWcKoAl+ziTRGC0vvB6/2uKveGVp6P7wV8OcBmc+2ycsbes3XnjPO6vwEasCrXa5rcLxCi60qwCLuMNC7S0evpYX3cPgCs4UQBNow+RtjJYXJgU8ZBCqyIUEV92qxpQ731LBzZ739m0byso3VlkS4NFvmr0OobDb3QNXcAZ3mvAHtGEVjPXQwnDTd2L+YGKSEYuq/VnF4Km8almJfBLTvC8mik6DKTPmSZpXNcJmvu1R1HAFZ3CnafOHNkxshpYXuvN6WNT1oCDKpBnZn4xf7TaKUzyHJK/EYFxe89Tg0GL05DnupKbe5eyBhSxIbj+ihis4g7ssgSbQhkldELS8cKWuj/vDmejhD6k8CvT+QxjEFng/xSPKq98Z4KLihZ9HSoHEankd9ySOg0Nccwx4QRsWNVzBGdxlCTSBNkzckGh54Z56pEUQ0/kk2OpOxZ6r6rcMaNpHCtPMxgOSA76WkMzr3h3o8B1zqbOSHIg4TGEPHvag4SHPsK2E9+4JWi8X2rAKbhoZADIHLERy60e6AKHjHqtv0WrB976aYJuvqyfElktIFNuHkqFyy4eD3AlP1Es1p2q39Q3loTuuqfUyD+SybWmtooYzuFMAbVjlWU4OgMVEuRW774uhqn3W3ndVC8E9EvO5Qs4pxAkbgWJOlCbrKZcp70oVpjslFPOil/tJyGktY+A1TgB+WCvDwDtg88AZ3GnOT2gDjZiMx0rUBL6rctmGSQjlDreI31f2pIntDzvjd+3lKPvlE9TTo+oTbwlijg+UW8JwCwfxutBW/w+cwR0canJP0UjrEvV/dHWacQkR18yJONFqn4ia2AQytpPCXLlKbhgx3T301TCp81HDNT6v6nxdueEM7jRuPbSBRlqVqPHdsp/U5NvhHlKI2rJuXk9HA80RrDx5AEoDn/V8n6YClaG488htCGdwp+oGgTYWaeOiJjAesvJC1IXsb8/uLh6MGkWMtT0IZDr+8Qa2JLoWyvaihjtVcsMHEhD2n9NTUSdyqt9Syhqw180jyLZkKH5wfOIIuzWKmr4ytNHArdl2RV3cOAXj5EGQTstIONx8PJe80k+u0ae7PAElCrAVh0mE3dpETfxHU7JAWxZ1mRhkXa8rtT4MdKOrSxxdA4hvzgMQa81e1qCRk4Wo0z11Xj6sjIibBFkqMN37+RCHl4LA96TwUek43iSkR+WbqFNRl8rJeZ0rZPthIGqLBkaU+yJAn5Wcy5PbPhzsvpRXatWQaW4cIaz+wAUOB9vEVms4g7vS9uD9+LhsrKp4DTHNaxiW0LJILGZrQjwHbkCETiYM7kBaQ9z4/iD33E8jHPtfLleotWcAClCG0XKJ2A7O4A4OFa327ERtFfvByZbINVVRlh1vqTS4UfQfQ0I2DCsn/RGposqrmeKQO99a4QhTpYHmZxXj3MCxM2PbulBwkguagmRuFOEODjVRjWjENPZjpEXsxwPEfigC7gnd5DRvT4T/WG0Oc+x9mR+ReyvI/2brcqK0ZqO4OyV/cwGH1zUSihdhTnAHh9niAbvYj5Fm8dRkCxOCqcHxj9XTbbXNB9YX/D/AnuKOvIYpHUYJYFa7iGWE57ndbq8krNX7fOAM7hRAG2jEJJ7aLPOFVemc5/uqDzzhPqvdPazehKFudm2p+6422mXP5a/2T+RNB2dwpwDaQCOmmS/vWNTSOPiBGg5HqnBEMlBC91S7fBAlSQXdIxR+f+SbYbzSfR+W4UwTZowm0AYasctRJAPXwKdLtBvVezRunuRvFZO/xeR6nq2EuqzvZxXjfb/p4ArONO5bNIE2rHh+yKzuBwckPAF1w6erbhXD/oW+A5gIEGKV5JX4Rw8HMWt/MLYjBJba0+qr88UlRrnAczY59oC7LIEm0IZJNjlaNqvQBPlrKP+CZ0mBmB1v9lv3o4OsjLwG8USc8ESDu+iVfrTIaPFcKPtFcvAOuFe6hMneFzKsD5KLibuLK3kNSgdO4RLEZ2oYXMEZ3GneyGaeGrRsVkuPQBX8tbhuNLjgpX6siN5WaCoFbXJNaVZN5fEpX/POAFKuzMdGVsjJT+lSy6qk+CKr5poePSBwBWcKoAm0gUZMaumZVj1dWg4tNIzX4KVuo4ivgBwvPmRIef6nkU4BMsUJ/bRNVDiPnoW1ToNKyTwp8Jg9BEdwBWcKoAm0YVb11LI+NZ215Mq4UuMBIXYC95aXvolrhmlIBN9rcNvHg7kNM/3Dw4tB22QFmAfxKL4uYOAIruBM4/lAE2jDpD61eScBLgIIAs/0n6JJQmXvyo2bl+pCvAZfUa40xALTTxBSLftOXqh6rRNPQd5fV2+1ROAIruAsS6AFNGFySYSGzXu+MHhaITzH61132wSpXojh93R8vdEpQENPiXWoYG9tIiBWWm4aH9eVyKVQu1c/NbaDKwXQApowyXhBw+bduSCcPddlyh4m3KitJpFfa5qf4rnO797Ub3ueLrDoVilQs4gBOdiM/TqH1yHjZykP2X29HbLhBo7gSgO0gCYK7bpz2fdRZN+63c3lqkuY2SIw6kWzWvm4PMBbUM2+UAHK/m57Qzl7w7iq+DcTzRaCQjca0CZju5vKsbcPUcMNHMGV5tIFLTBGyz6K9h1vOUzRGP4HST1SgPBMBOPlBI9P+LGvh0fJE6T1WmzpVKz6xGQjlom6xvwkshJP7e2QCDdwpAAaQAvYyrrjrX1vcm7q6AyrAOW5OM3idvMS5E6s88wIQfwvdR2Frzus1sofSbQ8SJIL/n16V7ff3dXq4u+hm4zDq7dwWriBIwXQAFrw15sc0InfKgKNgxWnZAXIHmHlgnTzjrcY+61eo10UfFszgWwX4i74HFUJXwTCHwRhmMx37GS1oLk44m3hJewULuCEsSoA92gALZiMC+0Gv4UOHcvXJHPAoJk9Blc3kvypfhIeBvMAJ/ayiGonMfq0WdFSrvC/0qSTfEWugMNoO/6bV3WLhy0LBzr+m+0CzetzqS1y8pMN/GEgOC9nEDiBGwXgHg2gBZNsF7Qb/A4IQ/3B6tqXlCMNFjhaJddBmJfC67jDuFjJBfQ+4TLi9R6jHS3jTnmqwe0vvtwD7mt6uI0kE51X8SvdRuec8fJeZozXFs5wAScL9OlmZgUh0WzwRyjoVHKjVeALAfCjp8zRvtpZ8cxbZhSG+1rcaZTabQWgdwqrM3t6L2EFcAAXcKIAnMO9WaAamv1jUXcp2cbMy3B2d3WcRdjYnqRSXGfmwmZbsLX0IyEpNp8xftpcYq699ZzE9nAAF0rAOdybjRPNBn8G+cZqq7a/B99fE6XqJ3tTVgpPNfS6O9oTj5o02+UjSHA94uE6xum1YREcwIUScG7Zlrs6yALhlbnN9S+nX8oDAGWPEG9ZHawoS4lgiBOmPG0egdoarNBkx3htdY3t4UAJuIZzM/85Ws1O1B2LtjQyDu4g6Q/SoC/YMnw6YpOr2SKv7TI2uqpUDmNjXfIguH6c2/zaMg60XhuSYnPsAQdKwDWcm40NrQbZYMtzSv8hPtZeNnXXMo4DQxGRezrgMiPjGgN7SyLglg4XFn5Z4pWTQF/xkFzxWiMHQvb8jMtrmTVsju2VgGO4hnOTsaFRtBpkCzFcJysjsRc8/Rl9wyCCjg66rwafsldhSzYF2xFe9+Kaagzjh+1Bi467JAZk02tLid/23kEAG2NrbI7tlYBjuDbjCo0GGvy3S/kq8oNTrHzCBLMTO6EFrZbXDffmPoUdNjxt7r1Ck3kSY6l6GicITPqudqLr8majo8ANlzWrcMjyXwgTG2NrbB4xLsZ03z8FjQZayA++ahgQQxcq8v4ixVuwciKuwoRKiTF+GvFsJiGix0p1os4iwndl791dfNxTRJhZi1jKIPxQP5HXO0m+0rqtii0G+1AyrhOpjYdNsS02xtZKwCncWgekvRrowWpdtLtlIUVup17vidHUYI+rKF9rt3pzWbO4jAMRElK52Jnd3WeSGZMtPpfvxbPAfpktBkLAfaaYl03uoT6+AwA4hVs4Nhsj2gwi4eh3/iYfkLEyHL7LLa8vc2MiBPFMltDMvWRVW9xM2PotFST+9eSfNWUh+F5+Jm/ayWFLbIptsbEScAmncGvJSwZtBlFR2ClzgmXEF3/R1LeIgsbRM9l3ErCuuG009ufq+kfyvXlTPxAbYktsim0jAC7Ng6vQZJALNrypbjH5oP6WGd3EE3TtEy3egppzq8sWIFwZUlHn+ObEltg0AuAQLq2zb/qjySBXyIQ7Wu5N8QPvItfSnPyj4O2iMVzhsi+NsEKkog6DorAhtozqtYFDuDQ9vKPFIA5sfFW3peUDx1salgzjG94b6EC0w8loVgll08xU1NgKm2E7bBgRcAeH1uMdjxaDeIB7L9PFcsAclHD04xaLireKxgg5VOzPZiuSihobYStshu0iAs7gzvywiwaDOME+Rkpa9bPchqzIgU8M3UjuW/QVGz8vr1PK4qai/oOSweG1ey4rNFzBGdyZbjvQHhoM4kZB55ILfdRm2z+s+hO9aeaE5s/yfXlBv+0vq7L2U/O9/Iz39hzYBhthq4iAI7jyUvsQ7QUGYLX+pxik1tqtxEUGJbdyQaZxsttBakwsocjyjq/V3jhNqTCvIbXYAptgG2yUA+AIrszdqWgO7QVWoAqOeXKn1Fjmdu2BL4fmGhAkoY/1GN5bIXfiQw5/sNZNyuLigu/he331P8QG2AKbYJscADdwBFfm40ZzgTUoxGeeLxhmZLPfywVzJbrsto8GSwhkz+aKT4XG7jESYfeSgjRPfz+ClZiDVIuHr/FvfA/fy89YX+czd2yALbBJDoATuIEj+20HRR99YL3OpevKL5xt7TslFpeDDFVGcwVRY9vfVN4cwumhIRHjp6o/lxEtHr7Gv/nopMVcmTNzxwY5Ai7ghPH7uBOYjdYCPyDYKXOljz0gBiSmguaXuWK05BxeLgUK8cmSsLqucf8WBMXY12358DX+zdRuzI05MlfmzNxzBBwwdjjxckZBY4FPEFBCkWsfPVmog4wIuvae5OIAhWP2ubspGIoVx5Yg/75n5sTcmCNzjQHYHg7gAk7M54G20FjgGx26FG3niyga2mPUnv3iqckxWxrvPP3DCLf1jWXEDXNYM47Pto9/Zg7MhTkxN+YYB34WQVOaDQ58LQBoK0gKFObzFUFGwA1Xum9weIwHlEGgKhN9/RAE2Rr8vlaxeodjZMyMnTkwlzhLO2BrVn4ef5GQFHtMFu4vxLd6EnbzAeyp74e7ODFMCpw/+NUw8d9WcLDi97B/VKQj+Y/dZoyMlTEzduYQI7BxeKD1GtqbQVNB0ujQuWh9ivT5Wp3wIEDmtdKaYT7pYDFi3NS57p3MGHfC4/W80slEUTQENZ97izExRsbKmGMENsW22Bhb+5z7ZLQU5AnwXZ/tk1xWKm7GTpKKn+OozxczFixwFLIhbzAsz5vJh30zregYE2NjjLEDW2JTbLuO57QyNBTkG8Twj/klmRgGfLBloWfEBmc/34cotES3ItS8xj1HzxQjYENsiU2xrddDM9oJFPDt5uvhOwWJwHRqH98jNTLmGyxfFIvc+OoSRUNQm6qxW0nu36QZc13MwGbYDhtiS++pcWhG5b7z7w3JrC0DHe55rymCK3JL01BTaj/XDou/2Mzed1UrbgBtbigZQ8zAVtgM22HDJM4Ow9FMkO8o6JzZOez05T2kcqmwcCI9CKk/ERNof6Fo52bTA5wxxARsg42wVZPNkpnXPLQStA5wjV58YpIJpJS7OuzB2t+4rElFjU2wDTZKMmEZjQStDWKsq5P05eL6QgyXvtrf1Q+f3t5FjQ2wBZ+FbRI9+KKNoLVCJvCQgVFUrdzoNbLhlSWEXXLoa2+iZs7MHRtgC2ySdNb9Q0Frh5xuH0/Sv1sQlgejrjOFWu6Wkz49ANu4qJkjc2XOzB0bYItE41vQQuAT/hML/K/c3JDR4ZY4iY5vNLryQVOz934kJ2p+d7beD+bE3Jgjc2XO/ldm/wH//rHWTT/+Ww4GXyiMYLpyk0lNvDHZG/QxfK94rBs9ec7vpl1tfUM5vuJE/dSMgbH8Bhg7c2AuzIm5MUfFymz7wD0aCNoaqPoutRueyqPgoDCWgtK5TT1eCKgnu4NyvMRA0CSeWtTUxkjwYMXvZgyMhTExNsbIWBkzY2cOzSG6+WRjOIf7oA0Dd9/D+RbCiWi44EAYHKRoOXykdMDa5samCp6KoCbTYCbGwpgYG2NkrIyZseuz5u0fuA48I//jsP1f4BDIQ6FEXvnEbzeJJT+SARgLY2JsjNG8MLv/uOjW7xW5hMmnT9t74DZor5CQzgPFCOPaDJnpMw5Og/YNVuyiTaRpelXrJjN94BAuQ1pTFHQsXU72hp+2TjLTB+7gMEjxm4kGN7cuQtMHzoIU2dTrywzKbzLTB44Ude5SbHRN0YpiuNfzltD0eR2OghRRuoOVnCsGHJM3ZKbPGDgJUuSGDa8qW09Sft5Nlsz0gQO4CFLEeb1ecgg11vySmT7YHNsHKQyj/TplrhWf6ARbMtMHG2Nrf9F1qV+7UFaQF2zITB9si42DFP6xfufizWRF+TAeMtMHW2LTIEU+rNwl+wsh70QjMn2wHTYMUuQfCjsWbSn7wFeEqIl/SmT6TMRW2CxIkf+gemaYjNC7JZHpg02wDTYKUrQ+rHVZxVJynXu0PJ8LmXPasZDnYANsgU2CFG0DhVdkCsKMm+J2JOZi5szcgxRt/SKnaPeCTiU3eqrS6r2KKHNjjkGK9of1Lu73r/W79OpQ2KXkHHk+o7G7CGNuKxLxXMbM2JkDc2FOQYoUAOx2049/l4Cd3UQs10mS60vhtfzsPBLxbMbE2BgjY2XMQYoU2UIavS+2XseiDaQQ+d4iovOpeB8W5WkM3YazDIQ7K/zsRn4Xv5PfzRgYC2MKUqSIs1tC4dWZZTl4UVtZ9q7HiUehi/h775H//4aI7z0OZ5LeVJvNw/fyM/wsn8Fn8Zl8Nr+D36Wvsp/if7BfCn8ECvocAAAAAElFTkSuQmCC'\n/** Bundled logo data-URI for a delivery-token symbol, or undefined when none\n * is bundled (callers fall back to their own icon rendering). Case-insensitive. */\nexport function bundledTokenLogo(symbol: string): string | undefined {\n  switch (symbol.toUpperCase()) {\n    case 'ONEUSD':\n      return ONEUSD_LOGO\n    case 'PONEUSD':\n      return PONEUSD_LOGO\n    case 'USDT':\n      return USDT_LOGO\n    case 'USDC':\n      return USDC_LOGO\n    default:\n      return undefined\n  }\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/components/Dropdown.tsx\n// (v0.1.0). Mechanical substitution only: CSS Modules `styles.x` references\n// become literal \"rd-x\" class names (see ../styles/component.css's header\n// mapping table) and the CSS Modules import becomes the plain side-effect\n// import below. One addition beyond the mechanical rename: `data-vaul-no-drag`\n// on the listbox <ul>, so a vaul drawer host (e.g. RelayDeposit's mobile\n// ResponsiveShell) doesn't mistake a pointer drag over the open popup for a\n// drawer-dismiss swipe (see WalletInfoContent.tsx's `wi-scrollable-body` for\n// the existing convention this follows). Everything else -- the ARIA\n// \"Collapsible Dropdown Listbox\" pattern, keyboard handling, outside-pointerdown\n// close, and the option-click preventDefault() guard against label-forwarded\n// re-toggling -- is the original logic, untouched.\n//\n// Accessible custom dropdown used in place of a native <select> wherever an\n// option needs a leading icon (native <select>/<option> can't render\n// per-option images/SVGs in most browsers). Implements the ARIA\n// \"Collapsible Dropdown Listbox\" pattern: a single-select popup button whose\n// focus never leaves the trigger -- the currently-highlighted option is\n// communicated via `aria-activedescendant` rather than moving DOM focus,\n// which is what lets Enter/Space/Arrow keys keep working straight off the\n// button.\n//\n// 2026-08-10: 팝업만 radix `Popover` 로 옮겼다. 예전엔 `position: absolute` 로\n// 떠서 스크롤 컨테이너(`.rd-wizard`)의 스크롤 높이에 그대로 더해졌고 —\n// 목록을 펼치면 다이얼로그에 세로 스크롤바가 생기고 목록은 아래에서 잘렸다.\n// Popover 는 body 로 portal 하고 공간이 모자라면 위로 뒤집으므로 둘 다 사라진다.\n// 포커스는 계속 트리거에 머문다(`onOpenAutoFocus` 차단) — 위 aria-activedescendant\n// 패턴이 그대로 성립해야 하기 때문.\nimport { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport * as Popover from \"@radix-ui/react-popover\";\nimport { ChevronDownIcon } from \"../../wallet-info/components/icons\";\nimport \"../styles/component.css\";\n\nexport interface DropdownOption {\n  value: string;\n  label: string;\n  icon?: ReactNode;\n  /** Optional muted second line under `label` ON THE CLOSED TRIGGER only\n   * (\"Network\", \"18 decimals\", \"on ONEchain\", ...). Added by the 2026-07-30\n   * redesign: chain and token became two half-width selectors on one row with\n   * no field label of their own, so each has to say what it is. Deliberately\n   * NOT rendered inside the popup list -- there the options are all the same\n   * kind of thing, and a second line per row just makes the list harder to\n   * scan. */\n  subLabel?: string;\n}\n\nexport interface DropdownProps {\n  value: string;\n  options: DropdownOption[];\n  onChange: (value: string) => void;\n  disabled?: boolean;\n  ariaLabel: string;\n  id?: string;\n}\n\nexport function Dropdown({ value, options, onChange, disabled, ariaLabel, id }: DropdownProps) {\n  const generatedId = useId();\n  const baseId = id ?? generatedId;\n  const listboxId = `${baseId}-listbox`;\n\n  const [open, setOpen] = useState(false);\n  const selectedIndex = options.findIndex((o) => o.value === value);\n  const [activeIndex, setActiveIndex] = useState(selectedIndex >= 0 ? selectedIndex : 0);\n\n  const buttonRef = useRef<HTMLButtonElement>(null);\n  const optionRefs = useRef<(HTMLLIElement | null)[]>([]);\n\n  const isDisabled = Boolean(disabled) || options.length === 0;\n  const selected = selectedIndex >= 0 ? options[selectedIndex] : undefined;\n\n  // 바깥 클릭 닫기는 Popover 가 담당한다(예전 document pointerdown 리스너 제거).\n\n  // Keep the highlighted option scrolled into view as it moves via keyboard.\n  // Guarded: jsdom (used in tests) doesn't implement scrollIntoView at all.\n  useEffect(() => {\n    if (!open) return;\n    optionRefs.current[activeIndex]?.scrollIntoView?.({ block: \"nearest\" });\n  }, [open, activeIndex]);\n\n  // 팝업은 body 로 portal 돼서 `.rd-root[data-theme]` 밖으로 나간다 — 열 때\n  // 트리거가 서 있는 테마를 읽어 팝업에 그대로 찍어준다.\n  const [popoverTheme, setPopoverTheme] = useState<string | null>(null);\n\n  function openList() {\n    if (isDisabled) return;\n    setPopoverTheme(\n      buttonRef.current?.closest(\"[data-theme]\")?.getAttribute(\"data-theme\") ?? null,\n    );\n    setActiveIndex(selectedIndex >= 0 ? selectedIndex : 0);\n    setOpen(true);\n  }\n\n  function closeList(refocus: boolean) {\n    setOpen(false);\n    if (refocus) buttonRef.current?.focus();\n  }\n\n  function commit(index: number) {\n    const opt = options[index];\n    if (!opt) return;\n    onChange(opt.value);\n    closeList(true);\n  }\n\n  function handleButtonClick() {\n    if (isDisabled) return;\n    if (open) closeList(false);\n    else openList();\n  }\n\n  function handleButtonKeyDown(e: KeyboardEvent<HTMLButtonElement>) {\n    if (isDisabled) return;\n    switch (e.key) {\n      case \"ArrowDown\":\n        e.preventDefault();\n        if (!open) openList();\n        else setActiveIndex((i) => Math.min(i + 1, options.length - 1));\n        break;\n      case \"ArrowUp\":\n        e.preventDefault();\n        if (!open) openList();\n        else setActiveIndex((i) => Math.max(i - 1, 0));\n        break;\n      case \"Enter\":\n      case \" \":\n        e.preventDefault();\n        if (!open) openList();\n        else commit(activeIndex);\n        break;\n      case \"Escape\":\n        if (open) {\n          e.preventDefault();\n          closeList(true);\n        }\n        break;\n      case \"Tab\":\n        // Let Tab's default focus-move happen; just collapse the popup.\n        if (open) setOpen(false);\n        break;\n      default:\n        break;\n    }\n  }\n\n  const activeOptionId = open && options[activeIndex] ? `${baseId}-option-${activeIndex}` : undefined;\n\n  return (\n    <Popover.Root open={open} onOpenChange={(next) => (next ? openList() : closeList(false))}>\n      <div className=\"rd-dropdown\">\n        <Popover.Trigger asChild>\n          <button\n            type=\"button\"\n            id={baseId}\n            ref={buttonRef}\n            className=\"rd-dropdown-trigger\"\n            aria-haspopup=\"listbox\"\n            aria-expanded={open}\n            aria-controls={listboxId}\n            aria-activedescendant={activeOptionId}\n            aria-label={ariaLabel}\n            disabled={isDisabled}\n            onClick={handleButtonClick}\n            onKeyDown={handleButtonKeyDown}\n          >\n            <span className=\"rd-dropdown-trigger-content\">\n              {selected?.icon}\n              <span className=\"rd-dropdown-trigger-label\">\n                {selected?.label ?? \"\"}\n                {selected?.subLabel && <small className=\"rd-dropdown-trigger-sub\">{selected.subLabel}</small>}\n              </span>\n            </span>\n            <span className=\"rd-dropdown-chevron\" aria-hidden=\"true\">\n              <ChevronDownIcon size={16} />\n            </span>\n          </button>\n        </Popover.Trigger>\n\n      {open && (\n        <Popover.Portal>\n        <Popover.Content\n          className=\"rd-dropdown-popover\"\n          data-theme={popoverTheme ?? undefined}\n          side=\"bottom\"\n          align=\"start\"\n          sideOffset={5}\n          collisionPadding={12}\n          // 포커스는 트리거에 남는다 — 목록은 aria-activedescendant 로만 움직인다.\n          onOpenAutoFocus={(e) => e.preventDefault()}\n          onCloseAutoFocus={(e) => e.preventDefault()}\n        >\n        <ul\n          className=\"rd-dropdown-listbox\"\n          role=\"listbox\"\n          id={listboxId}\n          aria-label={ariaLabel}\n          tabIndex={-1}\n          data-vaul-no-drag\n        >\n          {options.map((opt, index) => {\n            const isSelected = opt.value === value;\n            const isActive = index === activeIndex;\n            return (\n              <li\n                key={opt.value}\n                id={`${baseId}-option-${index}`}\n                role=\"option\"\n                aria-selected={isSelected}\n                ref={(el) => {\n                  optionRefs.current[index] = el;\n                }}\n                className={[\n                  \"rd-dropdown-option\",\n                  isActive ? \"rd-dropdown-option-active\" : \"\",\n                  isSelected ? \"rd-dropdown-option-selected\" : \"\",\n                ]\n                  .filter(Boolean)\n                  .join(\" \")}\n                onPointerEnter={() => setActiveIndex(index)}\n                // preventDefault: if a host wraps this dropdown in a <label>,\n                // a plain click on this (non-interactive) <li> would trigger\n                // the label's activation behavior -- a synthetic click\n                // forwarded to the trigger button -- re-toggling the popup\n                // open right after selection. Cancelling the default click\n                // behavior suppresses that forwarding regardless of host\n                // markup.\n                onClick={(e) => {\n                  e.preventDefault();\n                  commit(index);\n                }}\n              >\n                {opt.icon}\n                <span className=\"rd-dropdown-option-label\">{opt.label}</span>\n                {isSelected && (\n                  <span className=\"rd-dropdown-option-check\" aria-hidden=\"true\">\n                    ✓\n                  </span>\n                )}\n              </li>\n            );\n          })}\n        </ul>\n        </Popover.Content>\n        </Popover.Portal>\n      )}\n      </div>\n    </Popover.Root>\n  );\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/components/QuotePreview.tsx\n// (v0.1.0). Mechanical substitution only: CSS Modules `styles.x` references\n// become literal \"rd-x\" class names (see ../styles/component.css's header\n// mapping table), the CSS Modules import becomes the plain side-effect import\n// below, and viem's `formatUnits` is replaced by ../lib/units's\n// self-implemented equivalent (dapp-ui may depend on neither wagmi nor viem).\n// Everything else -- props and layout -- is the original logic, untouched.\n//\n// Purely presentational -- receives the live quote (and its loading/error\n// state) from <RelayDeposit>, which owns the useDepositAddress call. In the\n// address-first flow (Task 4) this preview is entirely optional: the\n// deposit address never waits on it, so callers only render this section\n// once an amount has actually been entered.\n//\n// 2026-07-30 redesign (\"variant A\"), presentation only -- same name, same\n// props, same states: the \"2. Quote preview\" CARD became an accent-tinted STRIP\n// that <DepositPanel> renders directly under the amount input, so \"send this ->\n// get that\" reads as feedback on the number just typed. (It sat above a\n// \"Continue\" button until the 2026-07-31 single-view collapse; the strip's own\n// position and every state below are unchanged.)\n// Every state the card had survives, re-cut to fit:\n//   loading  -> a spinner line inside the strip's slot\n//   error    -> the danger callout, in place of the strip. Purely informational:\n//               a broken quote never hides or blocks the deposit address, which\n//               the quote endpoint has no part in minting.\n//   belowMin -> the same warning callout, under the strip\n//   parity / fee / ETA -> one muted `.rd-quote-strip-hint` line under the strip\n//   no quote yet -> the same muted \"Enter an amount to see a quote.\" line\nimport { formatUnits } from \"../lib/units\";\nimport type { Destination, QuoteResult } from \"../lib/types\";\nimport \"../styles/component.css\";\n\nexport interface QuotePreviewProps {\n  quote?: QuoteResult;\n  isLoading: boolean;\n  error?: Error;\n  humanAmount: string;\n  originSymbol?: string;\n  destination: Destination;\n}\n\n/** Formats an API-supplied base-unit amount, or \"--\" if it is not a valid integer.\n *\n * BigInt() THROWS on anything that is not an integer string -- \"\", \"1.5\", null\n * coerced to \"null\" -- and this call sits inline in JSX, so an unhandled throw\n * during render unmounts the entire widget tree: one malformed field in a quote\n * response would blank the page rather than degrade one line of it (upstream\n * 01a1387). Every other formatUnits call site in this feature already guards. */\nfunction formatQuoteAmount(raw: string | undefined, decimals: number): string {\n  if (!raw) return \"--\";\n  try {\n    return formatUnits(BigInt(raw), decimals);\n  } catch {\n    return \"--\";\n  }\n}\n\nexport function QuotePreview({ quote, isLoading, error, humanAmount, originSymbol, destination }: QuotePreviewProps) {\n  return (\n    <div className=\"rd-stack\" data-rd-quote-preview=\"\">\n      {/* Same strip container as the loaded state, so the preview area holds\n          its height instead of jumping when the quote lands. */}\n      {isLoading && (\n        <div className=\"rd-quote-strip\" data-testid=\"rd-quote-skeleton\">\n          <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"30%\" }} aria-hidden=\"true\" />\n          <span className=\"rd-quote-strip-arrow\" aria-hidden=\"true\">\n            →\n          </span>\n          <span className=\"rd-skeleton rd-skeleton-line\" style={{ width: \"30%\" }} aria-hidden=\"true\" />\n        </div>\n      )}\n\n      {!isLoading && error && (\n        <div className=\"rd-callout rd-callout-danger\">Couldn&apos;t fetch a quote: {error.message}</div>\n      )}\n\n      {!isLoading && !error && quote && originSymbol && (\n        <>\n          <div className=\"rd-quote-strip\">\n            <span className=\"rd-quote-strip-side\">\n              <b>\n                {humanAmount} {originSymbol}\n              </b>\n              <small>You send</small>\n            </span>\n            <span className=\"rd-quote-strip-arrow\" aria-hidden=\"true\">\n              →\n            </span>\n            <span className=\"rd-quote-strip-side rd-quote-strip-end\">\n              <b>\n                ~{formatQuoteAmount(quote.expectedCrossd, destination.decimals)} {destination.symbol}\n              </b>\n              <small>You receive</small>\n            </span>\n          </div>\n\n          {quote.belowMin && (\n            <div className=\"rd-callout rd-callout-warning\">\n              This amount is below the minimum the bridge will accept. Increase the amount to continue.\n            </div>\n          )}\n\n          {/* Named for what it is (upstream af35dd5): the bridge fee taken out\n              of the amount, which is what makes the \"receive ~\" figure above\n              smaller than what was sent. Says nothing about the user's own\n              origin gas -- that is a separate wallet charge quoted at signing\n              time, and `feeUsd` deliberately excludes it (see\n              QuoteResult.feeUsd).\n\n              Rendered only when present: an absent fee is UNKNOWN, and printing\n              \"$0.00\" for a figure nobody could compute would claim the route is\n              free. */}\n          {(quote.assumesParity || quote.feeUsd) && (\n            <p className=\"rd-quote-strip-hint\">\n              {quote.feeUsd && (\n                <>\n                  Bridge fee: ${quote.feeUsd}, already deducted from the amount above. You also pay your own\n                  network fee to send the deposit.{\" \"}\n                </>\n              )}\n              {quote.assumesParity && (\n                <>\n                  Assumes 1:1 parity between bridged USDT and {destination.symbol}; the final amount may differ\n                  slightly.\n                </>\n              )}\n            </p>\n          )}\n        </>\n      )}\n\n      {!isLoading && !error && !quote && <p className=\"rd-muted\">Enter an amount to see a quote.</p>}\n    </div>\n  );\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/components/SourceForm.tsx\n// (v0.1.0). Mechanical substitution only: CSS Modules `styles.x` references\n// become literal \"rd-x\" class names (see ../styles/component.css's header\n// mapping table), the CSS Modules import becomes the plain side-effect import\n// below, viem's `formatUnits` is replaced by ../lib/units's self-implemented\n// equivalent (dapp-ui may depend on neither wagmi nor viem), and the original\n// `BalanceInfo` type (an inline shape re-exported by the source's\n// useDepositAddress.ts) is expressed here as `RelayBalance & { formatted:\n// string }` -- the ported ../hooks/useDepositAddress.ts's `balance` field is\n// typed the same way rather than re-exporting a separate named type. One\n// addition beyond the mechanical rename: `data-vaul-no-drag` on the amount and\n// recipient <input>s, so a vaul drawer host doesn't mistake a text-selection\n// drag inside either field for a drawer-dismiss swipe. Everything else --\n// props, layout, and behavior -- is the original logic, untouched.\n//\n// Presentational: all state (catalog, selected origin, amount, recipient,\n// balance) is passed in as props by <RelayDeposit>, which is the only\n// component wired to useDepositAddress. The amount/recipient inputs are\n// omitted entirely (`showAmountInput`/`showRecipientInput` false) when the\n// host controls those values via RelayDeposit props -- see propsToOpts in\n// RelayDeposit.tsx.\n//\n// Task 4 (address-first): amount is now entirely OPTIONAL -- the deposit\n// address never waits on it -- so the label says so (\"Amount (optional)\")\n// instead of implying it's required to proceed.\n//\n// 2026-07-30 redesign (approved step-1 \"variant A\"), presentation only -- no\n// prop, handler or validation rule changed:\n//   * the \"1. Choose what you're sending\" card title is gone (it duplicated\n//     the new two-segment progress bar in the wizard header) and with it the\n//     `.rd-card` wrapper -- the step body is the step, so it no longer nests a\n//     surface inside the modal surface. `data-rd-source-form` replaces the\n//     title as the identifying hook;\n//   * chain + token became two half-width selectors on ONE row under a single\n//     \"From\" label, each carrying a `subLabel` (\"Network\" / \"N decimals\") that\n//     absorbs the old per-field label and the \"chain 56 · 18 decimals\" hint;\n//   * the amount value is typeset large (22px/700, see `.rd-amount-input`) with\n//     the MAX button inline after the symbol rather than floated over the\n//     input, and the two hint paragraphs under it collapse into ONE meta line.\n//\n// 2026-07-31: the meta line carries the BALANCE only. The minimum moved into the\n// checklist card under the address (`DepositChecklist.tsx`), which states the\n// EFFECTIVE minimum -- the larger of the ops-set CDN floor and the catalog's own\n// per-origin value -- so printing the catalog value here as well read as a\n// second, smaller minimum. (The \"your deposit address doesn't need an amount\"\n// sentence left earlier: the checklist's \"reusable\" line says it.)\n//\n// 2026-07-31 (user): the optional ONEchain RECIPIENT input left this file for\n// <DepositPanel>'s \"Deliver as\" block. Fixed recipient values are no longer\n// rendered there. What is left here is strictly the SOURCE: which chain, which\n// token, and optionally how much.\nimport { useMemo } from \"react\";\nimport { formatUnits } from \"../lib/units\";\nimport type { Catalog, OriginOption } from \"../lib/types\";\nimport type { RelayBalance } from \"../types\";\nimport { ChainIcon, TokenIcon } from \"./icons\";\nimport { Dropdown } from \"./Dropdown\";\nimport { explorerTokenLink } from \"../lib/explorer\";\nimport \"../styles/component.css\";\n\nexport interface SourceFormProps {\n  catalog: Catalog;\n  origin?: OriginOption;\n  onChangeOrigin: (origin: OriginOption) => void;\n  amount: string;\n  onChangeAmount: (value: string) => void;\n  /** False when the host controls `amount` via a RelayDeposit prop -- hides the input entirely. */\n  showAmountInput?: boolean;\n  /** Connected wallet's balance of the selected origin token. */\n  balance?: RelayBalance & { formatted: string };\n  balanceLoading?: boolean;\n  /** Set when the entered amount exceeds the wallet balance. */\n  insufficientBalance?: boolean;\n  /** When provided, renders a \"Max\" button that fills the full balance. */\n  onMax?: () => void;\n  disabled?: boolean;\n}\n\n/** One entry per distinct chain present in the catalog, in catalog order. */\nfunction distinctChains(catalog: Catalog): { chainId: number; chainName: string }[] {\n  const seen = new Map<number, string>();\n  for (const origin of catalog.origins) {\n    if (!seen.has(origin.chainId)) seen.set(origin.chainId, origin.chainName);\n  }\n  return Array.from(seen, ([chainId, chainName]) => ({ chainId, chainName }));\n}\n\n/** Every token offered on the given chain. */\nfunction tokensForChain(catalog: Catalog, chainId: number | undefined): OriginOption[] {\n  if (chainId === undefined) return [];\n  return catalog.origins.filter((o) => o.chainId === chainId);\n}\n\n/** Formats a smallest-unit balance to a short human string (<=4 decimals).\n *\n * Truncates the already-exact `formatUnits` string rather than round-tripping it\n * through `Number` + `toLocaleString`: the latter rounds half-up (1.99999... ->\n * \"2\", i.e. MORE than the wallet holds) and silently loses precision above\n * 2^53, both of which CLAUDE.md §4 forbids for wei-scale values -- display is\n * floor, and a formatted string never re-enters arithmetic. */\nfunction formatBalance(value: bigint, decimals: number): string {\n  const s = formatUnits(value, decimals);\n  const dot = s.indexOf(\".\");\n  if (dot === -1) return s;\n  const truncated = `${s.slice(0, dot)}.${s.slice(dot + 1, dot + 1 + 4)}`.replace(/\\.?0+$/, \"\");\n  return truncated || s.slice(0, dot);\n}\n\nexport function SourceForm({\n  catalog,\n  origin,\n  onChangeOrigin,\n  amount,\n  onChangeAmount,\n  showAmountInput = true,\n  balance,\n  balanceLoading,\n  insufficientBalance,\n  onMax,\n  disabled,\n}: SourceFormProps) {\n  const chains = useMemo(() => distinctChains(catalog), [catalog]);\n  const tokenOptions = useMemo(() => tokensForChain(catalog, origin?.chainId), [catalog, origin?.chainId]);\n\n  function handleChainChange(next: string) {\n    const chainId = Number(next);\n    const firstToken = tokensForChain(catalog, chainId)[0];\n    if (firstToken) onChangeOrigin(firstToken);\n  }\n\n  function handleTokenChange(next: string) {\n    const match = tokenOptions.find((o) => o.token.address.toLowerCase() === next.toLowerCase());\n    if (match) onChangeOrigin(match);\n  }\n\n  const balanceDisplay = balance ? `${formatBalance(balance.value, balance.decimals)} ${balance.symbol}` : undefined;\n  const amountError = insufficientBalance\n    ? `Amount exceeds your balance${balanceDisplay ? ` (${balanceDisplay})` : \"\"}`\n    : undefined;\n  return (\n    // `data-rd-source-form`: a stable hook for tests/hosts now that the\n    // \"1. Choose what you're sending\" card title (the old identifying handle)\n    // is gone -- the numbered titles duplicated the step progress bar.\n    <section className=\"rd-stack\" data-rd-source-form=\"\">\n      {/* Chain + token on ONE row as two half-width selectors, under a single\n          \"From\" label: which network and which coin are one decision, and the\n          brand marks identify them faster than two stacked labelled fields. */}\n      <div className=\"rd-field\">\n        <div className=\"rd-field-label-row\">\n          <span className=\"rd-field-label\">From</span>\n          {origin &&\n            (() => {\n              const link = explorerTokenLink(origin.chainId, origin.token.address);\n              if (!link) return null;\n              return (\n                <a\n                  className=\"rd-token-explorer-link\"\n                  href={link.url}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  title={`View ${origin.token.symbol} on ${link.name}`}\n                >\n                  {link.name} ↗\n                </a>\n              );\n            })()}\n        </div>\n        {/* The row's own children are the Dropdowns' `.rd-dropdown` roots --\n            NOT <label>s: a <label> forwards clicks on non-interactive\n            descendants (the listbox <li> options) to its first labelable\n            control -- the dropdown trigger button -- so selecting an option\n            would immediately re-toggle the popup open. Each Dropdown carries\n            its own aria-label, so no accessibility is lost. */}\n        <div className=\"rd-field-row\">\n          <Dropdown\n            ariaLabel=\"Origin chain\"\n            value={origin ? String(origin.chainId) : \"\"}\n            onChange={handleChainChange}\n            disabled={disabled}\n            options={chains.map((c) => ({\n              value: String(c.chainId),\n              label: c.chainName,\n              subLabel: \"Network\",\n              icon: <ChainIcon chainId={c.chainId} chainName={c.chainName} size={22} />,\n            }))}\n          />\n\n          <Dropdown\n            ariaLabel=\"Token\"\n            value={origin?.token.address ?? \"\"}\n            onChange={handleTokenChange}\n            disabled={disabled || tokenOptions.length === 0}\n            options={tokenOptions.map((o) => ({\n              value: o.token.address,\n              label: o.token.symbol,\n              // Replaces the old \"chain 56 · 18 decimals\" hint line under the\n              // field: same fact, no extra row.\n              subLabel: `${o.token.decimals} decimals`,\n              icon: <TokenIcon symbol={o.token.symbol} logoUrl={o.token.logoUrl} size={22} />,\n            }))}\n          />\n        </div>\n      </div>\n\n      {showAmountInput && (\n        <label className=\"rd-field\">\n          <span className=\"rd-field-label\">Amount (optional)</span>\n          <div className=\"rd-amount-input\" data-rd-invalid={amountError ? \"\" : undefined}>\n            <input\n              type=\"text\"\n              inputMode=\"decimal\"\n              placeholder=\"0.0\"\n              value={amount}\n              onChange={(e) => {\n                let next = e.target.value;\n                if (next === \"\") {\n                  onChangeAmount(next);\n                  return;\n                }\n                // Keep the value a sensible decimal: digits + one dot, at most\n                // 4 fraction digits (reject the keystroke otherwise), a leading\n                // \".\" becomes \"0.\", redundant leading zeros collapse (\"007\" ->\n                // \"7\"), and the integer part caps at 12 digits -- larger than\n                // any real deposit, small enough to stop keyboard mashing.\n                if (next.startsWith(\".\")) next = `0${next}`;\n                if (!/^\\d*\\.?\\d{0,4}$/.test(next)) return;\n                next = next.replace(/^0+(?=\\d)/, \"\");\n                if ((next.split(\".\")[0] ?? \"\").length > 12) return;\n                onChangeAmount(next);\n              }}\n              disabled={disabled || !origin}\n              data-vaul-no-drag\n            />\n            {origin && <span className=\"rd-amount-input-suffix\">{origin.token.symbol}</span>}\n            {onMax && !disabled && (\n              <button\n                type=\"button\"\n                className=\"rd-amount-input-max\"\n                onClick={onMax}\n                disabled={disabled || !origin}\n              >\n                Max\n              </button>\n            )}\n          </div>\n          {/* One meta line instead of the old two hint paragraphs. Balance only:\n              the minimum is the checklist card's first line (see the header note\n              above), and the \"your deposit address doesn't need an amount\"\n              explainer is its \"reusable\" line. */}\n          {(balanceDisplay || balanceLoading) && (\n            <span className=\"rd-amount-meta\">\n              <span />\n              <span>{balanceLoading ? \"Balance: …\" : balanceDisplay ? `Balance: ${balanceDisplay}` : \"\"}</span>\n            </span>\n          )}\n          {amountError && <span className=\"rd-callout rd-callout-danger\">{amountError}</span>}\n        </label>\n      )}\n\n    </section>\n  );\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Block-explorer links per chain. The catalog (OriginOption) carries only\n// chainId/chainName/token -- no explorer URL -- so the token-detail link in\n// SourceForm is derived here from a small chainId -> explorer map. Unknown\n// chains return undefined (the UI then renders no link, gracefully).\n\ninterface Explorer {\n  // base is the explorer origin WITHOUT a trailing slash.\n  base: string\n  // name is a short human label for the link (e.g. \"BscScan\").\n  name: string\n  // kind distinguishes the token-detail path shape: EVM explorers use\n  // `/token/<address>`; Solana's Solscan uses `/token/<mint>` too, but is kept\n  // separate so a future SVM explorer with a different path is a one-line change.\n  kind: 'evm' | 'svm'\n}\n\nconst explorers: Record<number, Explorer> = {\n  1: { base: 'https://etherscan.io', name: 'Etherscan', kind: 'evm' },\n  56: { base: 'https://bscscan.com', name: 'BscScan', kind: 'evm' },\n  137: { base: 'https://polygonscan.com', name: 'PolygonScan', kind: 'evm' },\n  8453: { base: 'https://basescan.org', name: 'BaseScan', kind: 'evm' },\n  42161: { base: 'https://arbiscan.io', name: 'Arbiscan', kind: 'evm' },\n  612055: { base: 'https://scan.crosstoken.io', name: 'ONEchain Scan', kind: 'evm' },\n  792703809: { base: 'https://solscan.io', name: 'Solscan', kind: 'svm' },\n}\n\n/** explorerTokenLink returns the block-explorer token-detail URL + a short\n * explorer name for (chainId, tokenAddress), or undefined when the chain has\n * no known explorer (caller then renders no link). */\nexport function explorerTokenLink(chainId: number, address: string): { url: string; name: string } | undefined {\n  const e = explorers[chainId]\n  if (!e || !address) {\n    return undefined\n  }\n  // Both EVM explorers and Solscan use `/token/<addr>` for the token page.\n  return { url: `${e.base}/token/${address}`, name: e.name }\n}\n\n/** chainDisplayNames labels a chain in the transfer history's step rows.\n *\n * The catalog only names chains it lists as ORIGINS, but the history has to\n * label the bridge and delivery legs too (BSC and ONEchain), and ONEchain is\n * never an origin. Hence a small map here, with the chain id as an honest\n * fallback for anything unknown. */\nconst chainDisplayNames: Record<number, string> = {\n  1: 'Ethereum',\n  56: 'BNB Smart Chain',\n  137: 'Polygon',\n  8453: 'Base',\n  42161: 'Arbitrum',\n  612055: 'ONEchain',\n  792703809: 'Solana',\n}\n\n/** chainDisplayName returns a human label for chainId, e.g. \"Base\". Falls back\n * to \"Chain <id>\" so an unlisted chain still renders something truthful. */\nexport function chainDisplayName(chainId: number): string {\n  return chainDisplayNames[chainId] ?? `Chain ${chainId}`\n}\n\n/** explorerTxLink returns the block-explorer TRANSACTION URL + a short explorer\n * name for (chainId, txHash), or undefined when the chain has no known explorer\n * or the hash is empty (caller then renders plain text instead of a dead link).\n *\n * Separate from explorerTokenLink because the transfer history links one tx per\n * leg across THREE different chains -- the user's origin deposit, the BSC\n * bridge send, and the ONEchain delivery -- and each needs its own explorer. */\nexport function explorerTxLink(chainId: number, txHash: string): { url: string; name: string } | undefined {\n  const e = explorers[chainId]\n  if (!e || !txHash) {\n    return undefined\n  }\n  // Solscan uses /tx/<signature> as well, so one shape covers both kinds today.\n  return { url: `${e.base}/tx/${txHash}`, name: e.name }\n}\n","\"use client\";\n\n// The deposit modal's whole body, as ONE screen.\n//\n// History, part 1 (2026-07-31, user): this file replaced `steps/SourceStep.tsx`\n// + `steps/ExecuteStep.tsx`, the two halves of the old 2-step wizard:\n//\n//   \"스텝 1·2가 아니라 스텝 없이 1장으로. 어차피 QR을 주고 사용자가 알아서\n//    입금하는 거고 우리는 이걸 알 수 없다 — 대기/성공 팝업 불필요.\"\n//\n// The flow was never a commit-then-execute sequence: the deposit address is\n// minted from (recipient, origin, target) alone, the amount is optional, and a\n// manual send still has no local transaction receipt. It is now observed\n// indirectly through the backend's orders SSE + 1s REST snapshots, without\n// bringing the old step or full-screen completion state back.\n//\n// History, part 2 (2026-07-31, mockup \"A안 · QR 퍼스트\" then USER REVISION): the\n// QR-first pass replaced the selectors with a one-line summary plus a `change ▾`\n// disclosure, and the \"Deliver as\" field with a \"You'll receive →\" strip under\n// the address. The user rejected both compressions -- hiding the controls made\n// the choices look like read-outs -- so FROM and Deliver as are back as\n// always-visible labelled rows at the top, exactly as they were before that\n// pass. What SURVIVED from it is the part that consolidated ALARMS rather than\n// hiding controls:\n//\n//   * the checklist card (`DepositChecklist.tsx`) -- minimum + only-this-asset /\n//     reusable in one quiet card, replacing the `MinDepositNotice` block above\n//     the address, DepositStepView's \"Only send X on Y\" callout below it, and its\n//     \"reusable address\" sentence between them;\n//   * the locked card in the QR's own slot for the disconnected state, replacing\n//     the connect-prompt card that used to sit above the form;\n//   * the long reusable-address caveat later moved into a tooltip beside the\n//     Deposit address label; the detached footer/details blocks were removed.\n//\n// History, part 3 (2026-07-31, user): the address moved to the TOP and became one\n// row with its QR, and the delivery token joined the recipient into a single\n// statement. The column now reads:\n//\n//   live transfer status (only while useful)\n//   QR | address + Copy   (or compact address ↔ QR toggle / locked card)\n//   FROM (chain | token)  + optional amount / quote\n//   Deliver as (picker)   + recipient address\n//   checklist (send rules)\n//\n// Rationale, in the user's order of complaint: the thing every visit is FOR is the\n// address, so only actionable live status may precede it; the QR and the chip are the same value twice,\n// so they sit side by side rather than costing two vertical blocks; and \"which\n// token\" + \"where it lands\" are one sentence, so the picker and the recipient are\n// one block instead of two fields separated by the whole amount/quote area.\n//\n// Purely presentational: every value and handler comes from the wizard bundle\n// via <RelayDepositContent>, which owns no state either -- see\n// RelayWizardStateHost.tsx.\nimport type { Catalog, OriginOption, QuoteResult, Target } from \"../lib/types\";\nimport type { DepositState } from \"../hooks/useDepositAddress\";\nimport type { RelayBalance } from \"../types\";\nimport type { DepositArrivalStatus } from \"../providers/relay-wizard-provider\";\nimport { DepositChecklist, DepositStatus, type MinNoticeData } from \"./DepositChecklist\";\nimport { DepositAddressSkeleton, DepositStepView } from \"./DepositStepView\";\nimport { Dropdown } from \"./Dropdown\";\nimport { QuotePreview } from \"./QuotePreview\";\nimport { SourceForm } from \"./SourceForm\";\nimport { TokenIcon } from \"./icons\";\nimport \"../styles/component.css\";\n\nexport interface DepositPanelProps {\n  catalog: Catalog;\n  origin: OriginOption;\n  onChangeOrigin: (origin: OriginOption) => void;\n\n  amount: string;\n  onChangeAmount: (value: string) => void;\n  showAmountInput: boolean;\n  /** Send-rules data for the checklist card under the address: the effective\n   * minimum plus, when an earlier below-minimum deposit is still un-swept, the\n   * pending / remaining pair that replaces the minimum line. */\n  minNotice?: MinNoticeData;\n  /** Inline warning under the amount input (below-minimum / above-maximum).\n   * Advisory ONLY -- nothing on this screen is gated any more. */\n  amountHint?: string;\n  onMax?: () => void;\n\n  recipient: string;\n  onChangeRecipient: (value: string) => void;\n  showRecipientInput: boolean;\n  recipientValid: boolean;\n\n  balance?: RelayBalance & { formatted: string };\n  balanceLoading: boolean;\n  insufficientBalance: boolean;\n\n  /** Delivery targets offered, from useTargetTokens' `options`. */\n  targetOptions: Target[];\n  selectedTarget?: Target;\n  onChangeTarget: (symbol: string) => void;\n  /** The `showTargetSelector` prop; the picker still needs >1 option to render. */\n  showTargetSelector: boolean;\n\n  quote?: QuoteResult;\n  quoteLoading: boolean;\n  quoteError?: Error;\n\n  /** The resolved deposit address; undefined while loading or failed. */\n  depositAddress?: string;\n  depositAddressLoading: boolean;\n  depositAddressError?: Error;\n  /** Live SSE-derived funding/delivery projection for this address. */\n  arrivalStatus: DepositArrivalStatus;\n  /** Smallest-unit base-10 integer string, or undefined when no amount was entered. */\n  amountSmallestUnits?: string;\n\n  depositState: DepositState;\n  onDeposit: () => void;\n  /** `useDepositAddress`'s own gate, already ANDed with the host's opt-in. */\n  canDeposit: boolean;\n  /** The `showOneClickDeposit` opt-in (default off) -- when false the address\n   * section is manual-only and no in-flight progress card can appear. */\n  oneClickEnabled: boolean;\n\n  /** No identity yet (neither a connected wallet nor a usable recipient), so no\n   * deposit address can exist: the QR slot renders a locked card + Connect CTA\n   * instead. Replaces the old connect-prompt card above the form -- two \"you're\n   * not connected\" surfaces on one screen said it twice. */\n  locked: boolean;\n  /** Wired to the locked card's `Connect Wallet` CTA. Omitted -- the locked card\n   * still explains itself, with nothing to click (the recipient input, when\n   * rendered, is a wallet-free path to an address either way). */\n  onRequestConnect?: () => void;\n}\n\nexport function DepositPanel({\n  catalog,\n  origin,\n  onChangeOrigin,\n  amount,\n  onChangeAmount,\n  showAmountInput,\n  minNotice,\n  amountHint,\n  onMax,\n  recipient,\n  onChangeRecipient,\n  showRecipientInput,\n  recipientValid,\n  balance,\n  balanceLoading,\n  insufficientBalance,\n  targetOptions,\n  selectedTarget,\n  onChangeTarget,\n  showTargetSelector,\n  quote,\n  quoteLoading,\n  quoteError,\n  depositAddress,\n  depositAddressLoading,\n  depositAddressError,\n  arrivalStatus,\n  amountSmallestUnits,\n  depositState,\n  onDeposit,\n  canDeposit,\n  oneClickEnabled,\n  locked,\n  onRequestConnect,\n}: DepositPanelProps) {\n  // Same condition as the source widget: a single/fixed delivery token renders\n  // no picker regardless of `showTargetSelector`.\n  const showPicker = targetOptions.length > 1 && showTargetSelector;\n  const { phase, isSubmitting, isConfirming, isConfirmed, needsSwitch, reverted, error } = depositState;\n  // A pre-multi-target catalog (`targets: []`) resolves no Target at all, but the\n  // delivery token is still a fact the screen must state.\n  const deliverSymbol = selectedTarget?.symbol ?? catalog.destination.symbol;\n\n  return (\n    <>\n      {/* Live funding/delivery feedback stays at the very top of the panel so\n          a newly detected transfer cannot be missed below the QR and fields.\n          The static send rules remain in their supporting position below. */}\n      {minNotice && <DepositStatus minNotice={minNotice} arrivalStatus={arrivalStatus} />}\n\n      {/* ---- 1. QR + address (the top of the body) --------------------------\n          Rendered the moment the address resolves -- no click in between. The\n          loading skeleton has the exact same QR + address-card footprint, so an\n          origin/target change cannot resize the modal while the new address is\n          fetched. With no identity yet there is nothing to render but the reason\n          why -- in the QR's own footprint, for the same reason. */}\n      {locked ? (\n        <section className=\"rd-locked-card\" data-rd-locked=\"\">\n          <span className=\"rd-locked-glyph\" aria-hidden=\"true\">\n            <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" focusable=\"false\">\n              <path\n                d=\"M4 7.5A2.5 2.5 0 0 1 6.5 5h10A2.5 2.5 0 0 1 19 7.5v9a2.5 2.5 0 0 1-2.5 2.5h-10A2.5 2.5 0 0 1 4 16.5v-9Z\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.7\"\n              />\n              <path d=\"M4 9h15\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n              <path\n                d=\"M15 11.5h5v4h-5a2 2 0 0 1 0-4Z\"\n                fill=\"currentColor\"\n                opacity=\"0.18\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.5\"\n              />\n              <circle cx=\"15.7\" cy=\"13.5\" r=\"0.8\" fill=\"currentColor\" />\n            </svg>\n          </span>\n          <h3 className=\"rd-locked-title\">Connect your wallet</h3>\n          <p className=\"rd-locked-text\">Connect to get your personal deposit address.</p>\n          {showRecipientInput && (\n            <p className=\"rd-card-hint\">Or enter a recipient address to get one without connecting.</p>\n          )}\n          {onRequestConnect && (\n            <button\n              type=\"button\"\n              className=\"rd-button-primary rd-locked-action\"\n              data-track=\"connect\"\n              onClick={onRequestConnect}\n            >\n              <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                <path\n                  d=\"M5 6.5h11.5A2.5 2.5 0 0 1 19 9v8.5H7A3 3 0 0 1 4 14.5v-8A2.5 2.5 0 0 1 6.5 4H16\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"1.8\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                />\n                <path d=\"M15 10.5h5v4h-5a2 2 0 0 1 0-4Z\" stroke=\"currentColor\" strokeWidth=\"1.8\" />\n              </svg>\n              Connect Wallet\n            </button>\n          )}\n        </section>\n      ) : depositAddress ? (\n        <DepositStepView\n          depositAddress={depositAddress}\n          originChainId={origin.chainId}\n          originChainName={origin.chainName}\n          originCurrency={origin.token.address}\n          amount={amountSmallestUnits}\n          tokenSymbol={origin.token.symbol}\n          tokenDecimals={origin.token.decimals}\n          depositState={depositState}\n          onDeposit={onDeposit}\n          canDeposit={canDeposit}\n          oneClickEnabled={oneClickEnabled}\n        />\n      ) : depositAddressLoading ? (\n        <>\n          <DepositAddressSkeleton />\n          {oneClickEnabled && amountSmallestUnits && <span className=\"rd-skeleton rd-skeleton-cta\" aria-hidden=\"true\" />}\n          <p className=\"rd-sr-only\" role=\"status\">\n            Preparing your deposit address...\n          </p>\n        </>\n      ) : depositAddressError ? (\n        <div className=\"rd-callout rd-callout-danger\">\n          Couldn&apos;t fetch a deposit address: {depositAddressError.message}\n        </div>\n      ) : null}\n\n      {/* ---- 2. FROM (+ the optional amount input) -------------------------- */}\n      <SourceForm\n        catalog={catalog}\n        origin={origin}\n        onChangeOrigin={onChangeOrigin}\n        amount={amount}\n        onChangeAmount={onChangeAmount}\n        showAmountInput={showAmountInput}\n        balance={balance}\n        balanceLoading={balanceLoading}\n        insufficientBalance={insufficientBalance}\n        onMax={onMax}\n      />\n\n      {/* ---- 3. Deliver as + where it lands --------------------------------\n          ONE block, because it is one sentence: which token you get, and the\n          ONEchain address it is delivered to (user revision 2026-07-31 -- the\n          delivery token and the recipient used to be two labelled fields with the\n          whole amount/quote area between them, so nothing said they were about\n          the same leg of the transfer). The picker is only ever a picker when\n          there is actually more than one target to choose from. */}\n      <div className=\"rd-field\" data-rd-delivery=\"\">\n        <span className=\"rd-field-label\">Deliver as</span>\n        {showPicker ? (\n          <Dropdown\n            ariaLabel=\"Delivery token\"\n            value={selectedTarget?.symbol ?? \"\"}\n            onChange={onChangeTarget}\n            options={targetOptions.map((t) => ({\n              value: t.symbol,\n              label: t.symbol,\n              subLabel: \"on ONEchain\",\n              icon: <TokenIcon symbol={t.symbol} size={22} />,\n            }))}\n          />\n        ) : (\n          <div className=\"rd-static-target\">\n            <TokenIcon symbol={deliverSymbol} size={22} />\n            <span>{deliverSymbol}</span>\n          </div>\n        )}\n\n        {/* The recipient is entirely hidden in the normal fixed-recipient\n            presentation. Only hosts that explicitly opt into recipient editing\n            render an input here; no read-only address value is duplicated. */}\n        {showRecipientInput && (\n          <label className=\"rd-delivery-recipient\">\n            <input\n              type=\"text\"\n              placeholder=\"0x...\"\n              aria-label=\"ONEchain recipient address\"\n              value={recipient}\n              onChange={(e) => onChangeRecipient(e.target.value)}\n              data-vaul-no-drag\n            />\n            <span className=\"rd-card-hint\">\n              Defaults to your connected wallet. It can differ from the address you deposit from, and is also used\n              as the refund address if anything goes wrong.\n            </span>\n            {!recipientValid && <span className=\"rd-callout rd-callout-danger\">Not a valid address</span>}\n          </label>\n        )}\n      </div>\n\n      {selectedTarget?.external && (\n        <div className=\"rd-callout rd-callout-muted\">\n          Delivered as {selectedTarget.symbol} via an external adapter.\n        </div>\n      )}\n\n      {/* Amount feedback, in reading order under the input: the hard\n          below-min/above-max sentence first, then the live quote (whose own\n          loading/error/belowMin states live inside it). Both are advisory --\n          the deposit address below does not wait on either. */}\n      {amountHint && (\n        <div className=\"rd-callout rd-callout-warning\" data-rd-amount-hint=\"\">\n          {amountHint}\n        </div>\n      )}\n\n      {amount.trim() && (\n        <QuotePreview\n          quote={quote}\n          isLoading={quoteLoading}\n          error={quoteError}\n          humanAmount={amount}\n          originSymbol={origin.token.symbol}\n          destination={catalog.destination}\n        />\n      )}\n\n      {/* ---- Send rules -----------------------------------------------------\n          The dynamic transfer status now lives at the top of this panel. This\n          quiet rules card keeps its supporting position below the controls. */}\n      {minNotice && (\n        <DepositChecklist\n          minNotice={minNotice}\n          onlySend={{ symbol: origin.token.symbol, chainName: origin.chainName }}\n          showWalletCreditNote={!showRecipientInput && recipientValid}\n        />\n      )}\n\n      {/* One-click progress, inline under the address block that owns the\n          button. DepositStepView hides its whole one-click block once\n          `canDeposit` goes false -- which is exactly what a submit in flight\n          does -- so without this a user who clicked would get no feedback at\n          all. Strings are the ones DepositStepView itself uses. Never rendered\n          in the default QR-only flow: there is no button to have clicked.\n          Manual sends use the separate SSE-derived status slot above; this\n          card remains specific to the locally submitted one-click tx.) */}\n      {oneClickEnabled && phase !== \"idle\" && !canDeposit && (\n        <div className=\"rd-card\">\n          {isSubmitting && (\n            <p className=\"rd-muted\">\n              <span className=\"rd-spinner\" /> {needsSwitch ? \"Switching network...\" : \"Confirm in wallet...\"}\n            </p>\n          )}\n\n          {isConfirming && (\n            <p className=\"rd-muted\">\n              <span className=\"rd-spinner\" /> Confirming...\n            </p>\n          )}\n\n          {depositState.txHash && (\n            <p className=\"rd-card-hint\">\n              Tx: <span style={{ fontFamily: \"monospace\" }}>{depositState.txHash}</span>\n            </p>\n          )}\n\n          {isConfirmed && reverted && (\n            <div className=\"rd-callout rd-callout-danger\">\n              The deposit transaction reverted on-chain. Please check your wallet and try again.\n            </div>\n          )}\n\n          {isConfirmed && !reverted && (\n            <div className=\"rd-callout rd-callout-success\">\n              Deposit confirmed on-chain. Waiting for it to be swept...\n            </div>\n          )}\n\n          {error && <div className=\"rd-callout rd-callout-danger\">{error}</div>}\n        </div>\n      )}\n\n    </>\n  );\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/components/OrdersList.tsx\n// (v0.1.0), then REDESIGNED in place on 2026-07-30 (\"Transfer history — card\n// components, per-chain split\" mockup). The data plumbing is untouched — same\n// props, same GET /v1/orders?user= shape via useRelayOrders, same\n// formatting/explorer helpers — but the PRESENTATION changed twice over:\n//\n//   * the 6-column grid table is gone. A column grid could not survive the\n//     narrow surfaces this list actually lives on (a 26rem desktop modal, a\n//     mobile drawer): amounts wrapped mid-digit and the container query that\n//     hid columns just traded one illegibility for another. Each receipt is\n//     now a CARD — asset+status on top, sent → received amounts, date+chevron\n//     below — a vertical stack that has no columns to squeeze.\n//   * ONE CARD = ONE DEPOSIT, when the backend has attributed them. The\n//     sweeper aggregates every deposit sitting at the reusable address into\n//     ONE order paid out by ONE delivery, so a Base deposit and a BNB deposit\n//     used to melt into a single \"Multiple · 2 chains\" row. The user signed\n//     two transactions; they now see two cards, each naming its own chain and\n//     linking its own origin tx. The delivered amount is allocated across the\n//     split cards pro rata (see allocateProRata) and marked \"~\" — the split is\n//     presentation, the payout on-chain was one transfer.\n//\n// The card list intentionally has no header row and no list title of its own\n// unless asked (`showTitle`). In modal mode (`showTitle=false`) the list is also\n// frameless: the shell is already the surface and every transfer has its own\n// card, so another card around the whole stack is redundant.\nimport { useEffect, useId, useRef, useState, type CSSProperties } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { ChevronDownIcon } from \"../../wallet-info/components/icons\";\nimport { formatUnits } from \"../lib/units\";\nimport { chainDisplayName, explorerTxLink } from \"../lib/explorer\";\nimport type { Catalog, OrderDeposit, OrderSummary } from \"../lib/types\";\nimport \"../styles/component.css\";\n\nexport interface OrdersListProps {\n  orders: OrderSummary[];\n  isLoading: boolean;\n  error?: Error;\n  /** Used to format amounts in their own token's decimals (origin looked up by\n   * chainId + currency, delivery by the order's target symbol). */\n  catalog?: Catalog;\n  /** Render the list's own framed \"Transfer history\" section (default true).\n   * The history modal passes false: its shell already owns the accessible title\n   * and surface, so the list becomes a bare stack of transfer cards. */\n  showTitle?: boolean;\n}\n\nexport function HistoryEmptyState({ requiresIdentity = false }: { requiresIdentity?: boolean }) {\n  return (\n    <div\n      className=\"rd-history-empty\"\n      role=\"status\"\n      data-variant={requiresIdentity ? \"disconnected\" : \"empty\"}\n      data-testid={requiresIdentity ? \"rd-history-empty-state\" : \"rd-history-no-transfers\"}\n    >\n      <span className=\"rd-history-empty-icon\" aria-hidden=\"true\">\n        <svg width=\"34\" height=\"34\" viewBox=\"0 0 24 24\" fill=\"none\" focusable=\"false\">\n          <path\n            d=\"M7 3.8h7l3 3V12M7 3.8A2.2 2.2 0 0 0 4.8 6v12A2.2 2.2 0 0 0 7 20.2h5\"\n            stroke=\"currentColor\"\n            strokeWidth=\"1.7\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          />\n          <path d=\"M14 3.8V7h3\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinejoin=\"round\" />\n          <circle cx=\"16.5\" cy=\"16.5\" r=\"3.7\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n          <path\n            d=\"M16.5 14.5v2.2l1.4.8\"\n            stroke=\"currentColor\"\n            strokeWidth=\"1.7\"\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          />\n        </svg>\n      </span>\n      <h3 className=\"rd-history-empty-title\">\n        {requiresIdentity ? \"Connect to view history\" : \"No transfers yet\"}\n      </h3>\n      <p className=\"rd-history-empty-copy\">\n        {requiresIdentity\n          ? \"Connect a wallet or provide an address to see transfer history.\"\n          : \"Your deposits and their delivery progress will appear here.\"}\n      </p>\n      {!requiresIdentity && (\n        <span className=\"rd-history-empty-live\">\n          <span aria-hidden=\"true\" />\n          Updates automatically\n        </span>\n      )}\n    </div>\n  );\n}\n\n/** BSC chain id: the bridge leg always runs here, and an unattributed deposit's\n * arrival tx is a BSC tx. */\nconst BSC_CHAIN_ID = 56;\n\nfunction isTerminalFailure(status: string): boolean {\n  return status === \"FAILED\" || status === \"REFUNDED\" || status === \"RELAY_REFUNDED\" || status === \"EXPIRED\";\n}\n\n/** API lifecycle values are useful diagnostics, but not suitable primary UI\n * copy. Known states get task-oriented labels; an unfamiliar future state is\n * still made readable without hiding it entirely. */\nfunction orderStatusLabel(status: string): string {\n  const normalized = status.trim().toUpperCase();\n  const known: Record<string, string> = {\n    AWAITING_DEPOSIT: \"Waiting for deposit\",\n    BSC_FUNDED: \"Deposit confirmed\",\n    RELAY_PENDING: \"Bridging\",\n    RELAY_SUBMITTED: \"Bridging\",\n    BRIDGING: \"Bridging\",\n    CROSS_PENDING: \"Delivering\",\n    FINALIZING: \"Delivering\",\n    EXECUTING: \"Delivering\",\n    COMPLETED: \"Completed\",\n    RELAY_REFUNDED: \"Refunded\",\n    REFUNDED: \"Refunded\",\n    EXPIRED: \"Expired\",\n    FAILED: \"Failed\",\n  };\n  const label = known[normalized];\n  if (label) return label;\n  const words = normalized.toLowerCase().replace(/_/g, \" \").trim();\n  return words ? `${words[0].toUpperCase()}${words.slice(1)}` : \"Processing\";\n}\n\nfunction statusPillClassName(status: string): string {\n  if (status === \"COMPLETED\") return \"rd-status-pill rd-status-pill-success\";\n  if (isTerminalFailure(status)) return \"rd-status-pill rd-status-pill-failure\";\n  return \"rd-status-pill\";\n}\n\n/** Renders a smallest-unit amount string in human units, falling back to the\n * raw string when decimals aren't known (unmatched origin) or the value is\n * empty/malformed -- amounts are best-effort display, never load-bearing. */\nfunction formatAmount(raw: string | undefined, decimals: number | undefined): string | undefined {\n  if (!raw) return undefined;\n  if (decimals === undefined) return raw;\n  try {\n    return formatUnits(BigInt(raw), decimals);\n  } catch {\n    return raw;\n  }\n}\n\n/** displayAmount shortens a full-precision amount for a card, keeping the\n * exact value for the portalled hover tooltip.\n *\n * An 18-decimal token renders as e.g. \"0.968446245970557194\", which buries the\n * magnitude the figure exists to convey. Four decimal places are enough for\n * scanning, and truncating (not rounding) means the\n * shown figure never claims the user has more than they do. The untruncated\n * value stays one hover away. */\nfunction displayAmount(full: string, maxDecimals = 4): string {\n  const dot = full.indexOf(\".\");\n  if (dot < 0) return full;\n  const trimmed = full.slice(0, dot + 1 + maxDecimals).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n  return trimmed === \"\" ? \"0\" : trimmed;\n}\n\nfunction formatTimestamp(iso: string): string {\n  const d = new Date(iso);\n  return Number.isNaN(d.getTime()) ? iso : d.toLocaleString();\n}\n\nfunction shortHash(hash: string): string {\n  return hash.length > 14 ? `${hash.slice(0, 8)}...${hash.slice(-4)}` : hash;\n}\n\nfunction HistoryAmount({\n  amount,\n  symbol,\n  className,\n}: {\n  amount?: string;\n  symbol?: string;\n  className: string;\n}) {\n  const full = amount ? `${amount}${symbol ? ` ${symbol}` : \"\"}` : \"\";\n  const tooltipId = useId();\n  const anchorRef = useRef<HTMLSpanElement>(null);\n  const tooltipRef = useRef<HTMLSpanElement>(null);\n  const [tooltipOpen, setTooltipOpen] = useState(false);\n  const [tooltipStyle, setTooltipStyle] = useState<CSSProperties>({\n    top: -9999,\n    left: -9999,\n    visibility: \"hidden\",\n  });\n\n  useEffect(() => {\n    if (!tooltipOpen) return;\n\n    const positionTooltip = () => {\n      const anchor = anchorRef.current;\n      const tooltip = tooltipRef.current;\n      if (!anchor || !tooltip) return;\n\n      const anchorRect = anchor.getBoundingClientRect();\n      const tooltipRect = tooltip.getBoundingClientRect();\n      const viewportPadding = 12;\n      const gap = 8;\n      const maxLeft = Math.max(viewportPadding, window.innerWidth - tooltipRect.width - viewportPadding);\n      const left = Math.min(\n        Math.max(anchorRect.left + anchorRect.width / 2 - tooltipRect.width / 2, viewportPadding),\n        maxLeft,\n      );\n      const above = anchorRect.top - tooltipRect.height - gap;\n      const preferredTop = above >= viewportPadding ? above : anchorRect.bottom + gap;\n      const maxTop = Math.max(viewportPadding, window.innerHeight - tooltipRect.height - viewportPadding);\n      const top = Math.min(Math.max(preferredTop, viewportPadding), maxTop);\n      const computed = window.getComputedStyle(anchor);\n\n      setTooltipStyle({\n        top,\n        left,\n        visibility: \"visible\",\n        backgroundColor: computed.getPropertyValue(\"--rd-text\").trim() || \"#eef0f6\",\n        borderColor: computed.getPropertyValue(\"--rd-border\").trim() || \"#2a2d3a\",\n        boxShadow: computed.getPropertyValue(\"--rd-shadow\").trim() || \"0 8px 24px rgba(0, 0, 0, 0.35)\",\n        color: computed.getPropertyValue(\"--rd-surface\").trim() || \"#171923\",\n      });\n    };\n\n    positionTooltip();\n    window.addEventListener(\"resize\", positionTooltip);\n    window.addEventListener(\"scroll\", positionTooltip, true);\n    return () => {\n      window.removeEventListener(\"resize\", positionTooltip);\n      window.removeEventListener(\"scroll\", positionTooltip, true);\n    };\n  }, [tooltipOpen]);\n\n  if (!amount) return <span className={className}>-</span>;\n\n  return (\n    <>\n      <span\n        ref={anchorRef}\n        className={className}\n        aria-label={full}\n        aria-describedby={tooltipOpen ? tooltipId : undefined}\n        data-full-value={full}\n        onMouseEnter={() => {\n          setTooltipStyle({ top: -9999, left: -9999, visibility: \"hidden\" });\n          setTooltipOpen(true);\n        }}\n        onMouseLeave={() => setTooltipOpen(false)}\n      >\n        {displayAmount(amount)}{symbol ? ` ${symbol}` : \"\"}\n      </span>\n      {tooltipOpen && typeof document !== \"undefined\" && createPortal(\n        <span\n          ref={tooltipRef}\n          id={tooltipId}\n          role=\"tooltip\"\n          data-rd-history-floating-tooltip=\"\"\n          style={tooltipStyle}\n        >\n          {full}\n        </span>,\n        document.body,\n      )}\n    </>\n  );\n}\n\n/** What an UN-SPLIT card's asset line describes (single-deposit orders, plus\n * multi-deposit orders whose amounts failed to parse — see allocateProRata).\n *\n * Read from the DEPOSITS, not the order: the order records BSC USDT for every\n * standing deposit regardless of where the funds came from (one forwarder serves\n * every origin route). The order's own origin is the correct fallback for a\n * direct BSC deposit, and for any deposit the backend has not attributed yet.\n *\n * `routes` only matters on the fallback path now: a splittable order renders one\n * card per deposit and never consults it, but when the split is impossible the\n * single card must still say \"Multiple\" rather than naming whichever deposit\n * happened to be first. */\nfunction originOf(order: OrderSummary): { chainId: number; currency: string; routes: number } {\n  const attributed = (order.deposits ?? []).filter((d) => d.originChainId);\n  const distinct = new Set(attributed.map((d) => `${d.originChainId}:${(d.originCurrency ?? \"\").toLowerCase()}`));\n  if (attributed.length > 0) {\n    const first = attributed[0];\n    return { chainId: first.originChainId!, currency: first.originCurrency ?? \"\", routes: distinct.size };\n  }\n  return { chainId: order.originChainId, currency: order.originCurrency, routes: 1 };\n}\n\n/**\n * Splits the order's delivered amount across its deposits pro rata, entirely in\n * BigInt (CLAUDE.md §4 — wei-scale values never touch Number): each share is\n * `total * deposit / sum` floored by BigInt division, and the dust that\n * flooring shaves off is added to the LAST share so the shares always sum to\n * the exact total. Floor-then-top-up is deliberate: no card ever claims more\n * than its true share, and nothing the user received disappears.\n *\n * Returns undefined when any input fails to parse or the deposit sum is zero —\n * callers fall back to the un-split single card rather than guessing.\n */\nfunction allocateProRata(totalRaw: string | undefined, deposits: OrderDeposit[]): bigint[] | undefined {\n  if (!totalRaw) return undefined;\n  let total: bigint;\n  let amounts: bigint[];\n  try {\n    total = BigInt(totalRaw);\n    amounts = deposits.map((d) => BigInt(d.amount));\n  } catch {\n    return undefined;\n  }\n  const sum = amounts.reduce((a, b) => a + b, 0n);\n  if (sum <= 0n) return undefined;\n  const shares = amounts.map((a) => (total * a) / sum);\n  const allocated = shares.reduce((a, b) => a + b, 0n);\n  shares[shares.length - 1] += total - allocated;\n  return shares;\n}\n\n/** One deposit/bridge/deliver leg inside an expanded card. `shared` marks a tx\n * that several split cards point at (the sweep/delivery happened ONCE for the\n * whole order) — labeling it beats letting the same hash on two cards read as\n * a duplication bug. */\nfunction StepRow({\n  label,\n  chainId,\n  txHash,\n  amount,\n  shared,\n  missingLabel = \"Pending\",\n}: {\n  label: string;\n  chainId: number;\n  txHash?: string;\n  amount?: string;\n  shared?: boolean;\n  /** A completed/failed order with an absent hash means the detail was not\n   * recorded, not that the already-terminal step is still pending. */\n  missingLabel?: string;\n}) {\n  const link = txHash ? explorerTxLink(chainId, txHash) : undefined;\n  return (\n    <div className=\"rd-history-step\">\n      <span className={`rd-history-step-dot${txHash ? \" rd-history-step-dot-done\" : \"\"}`} />\n      <span className=\"rd-history-step-label\">{label}</span>\n      <span className=\"rd-history-step-chain\">{chainDisplayName(chainId)}</span>\n      {amount && <HistoryAmount amount={amount} className=\"rd-history-step-amount\" />}\n      <span className=\"rd-history-step-tx\">\n        {link ? (\n          <a\n            href={link.url}\n            target=\"_blank\"\n            rel=\"noreferrer\"\n            title={txHash}\n            aria-label={`${label} transaction on ${chainDisplayName(chainId)}`}\n          >\n            {shortHash(txHash!)}\n          </a>\n        ) : txHash ? (\n          // A hash we have but no explorer for is still worth showing -- the\n          // user can paste it somewhere. Silently dropping it would look like\n          // the step never happened.\n          <span title={txHash}>{shortHash(txHash)}</span>\n        ) : (\n          <span className=\"rd-muted\">{missingLabel}</span>\n        )}\n        {shared && txHash && (\n          <span className=\"rd-history-step-shared\" title=\"Shared by deposits processed together\">\n            combined transfer\n          </span>\n        )}\n      </span>\n    </div>\n  );\n}\n\n/** Everything one card needs, precomputed so the split and un-split paths feed\n * the identical renderer. */\ninterface HistoryCard {\n  key: string;\n  order: OrderSummary;\n  /** Chain/currency the asset line names (a deposit's own attribution, or the\n   * originOf fallback). */\n  chainId: number;\n  currency: string;\n  /** > 1 only on the un-split fallback path — renders the \"Multiple · N chains\"\n   * label instead of one arbitrary chain. */\n  routes: number;\n  /** Smallest-unit BSC USDT string: the deposit's own arrival on a split card,\n   * the order's whole amountIn otherwise. */\n  sentRaw: string | undefined;\n  /** Smallest-unit delivered amount (allocated share on a split card). */\n  receivedRaw: string | undefined;\n  /** True renders \"~\" + muted: the pre-delivery projection on an un-split\n   * card, and EVERY split card — a pro-rata share is an approximation even\n   * when the order's total was observed. */\n  receivedIsEstimate: boolean;\n  /** The one deposit this card's expanded view details. Unset = un-split card,\n   * which lists every attributed deposit (or the not-recorded note). */\n  deposit?: OrderDeposit;\n  /** Split cards share the order's bridge/deliver txs — label them so. */\n  sharedLegs: boolean;\n}\n\n/** An order becomes one card per attributed deposit when there are several and\n * the amounts parse; otherwise exactly one card, byte-identical in content to\n * the pre-split behavior. */\nfunction cardsOf(order: OrderSummary): HistoryCard[] {\n  const deposits = order.deposits ?? [];\n  const outRaw = order.amountOut || order.expectedOut || undefined;\n\n  if (deposits.length >= 2) {\n    const shares = allocateProRata(outRaw, deposits);\n    if (shares) {\n      return deposits.map((d, i) => ({\n        key: `${order.orderId}:${d.txHash}:${d.blockNumber}`,\n        order,\n        chainId: d.originChainId ?? BSC_CHAIN_ID,\n        currency: d.originCurrency ?? \"\",\n        routes: 1,\n        sentRaw: d.amount,\n        receivedRaw: shares[i].toString(),\n        receivedIsEstimate: true,\n        deposit: d,\n        sharedLegs: true,\n      }));\n    }\n  }\n\n  const { chainId, currency, routes } = originOf(order);\n  return [\n    {\n      key: order.orderId,\n      order,\n      chainId,\n      currency,\n      routes,\n      sentRaw: order.amountIn,\n      receivedRaw: outRaw,\n      receivedIsEstimate: !order.amountOut && Boolean(order.expectedOut),\n      sharedLegs: false,\n    },\n  ];\n}\n\nexport function OrdersList({ orders, isLoading, error, catalog, showTitle = true }: OrdersListProps) {\n  // Which cards are expanded, by card key. Collapsed by default: the card face\n  // answers the common question, and the legs are for when something looks wrong.\n  const [expanded, setExpanded] = useState<Record<string, boolean>>({});\n  const toggle = (key: string) => setExpanded((prev) => ({ ...prev, [key]: !prev[key] }));\n\n  return (\n    <section className={showTitle ? \"rd-card\" : \"rd-stack\"} data-rd-orders-list=\"\">\n      {showTitle && (\n        <div className=\"rd-card-header\">\n          <h2 className=\"rd-card-title\">Transfer history</h2>\n        </div>\n      )}\n\n      {isLoading && orders.length === 0 && (\n        <p className=\"rd-muted\" role=\"status\" aria-live=\"polite\">\n          <span className=\"rd-spinner\" /> Loading orders...\n        </p>\n      )}\n\n      {error && (\n        <div className=\"rd-callout rd-callout-danger\">\n          Couldn&apos;t load your orders: {error.message}\n        </div>\n      )}\n\n      {!isLoading && !error && orders.length === 0 && (\n        <HistoryEmptyState />\n      )}\n\n      {orders.length > 0 && (\n        <div className=\"rd-history-table\" data-vaul-no-drag>\n          {orders.flatMap(cardsOf).map((card) => {\n            const { order } = card;\n            const origin = catalog?.origins.find(\n              (o) => o.chainId === card.chainId && o.token.address.toLowerCase() === card.currency.toLowerCase(),\n            );\n\n            // The \"out\" side is the order's actual delivery target (e.g.\n            // pONEUSD), not the single default destination -- resolve it from\n            // catalog.targets by the order's target symbol, falling back to the\n            // default target, then to catalog.destination.\n            const deliveryTarget =\n              (order.target\n                ? catalog?.targets.find((t) => t.symbol.toLowerCase() === order.target!.toLowerCase())\n                : undefined) ?? catalog?.targets.find((t) => t.isDefault);\n            const outSymbol = deliveryTarget?.symbol ?? catalog?.destination.symbol ?? \"\";\n            const outDecimals = deliveryTarget?.decimals ?? catalog?.destination.decimals;\n\n            // BSC USDT decimals for the swept \"in\" amount. Both amountIn and a\n            // deposit's own `amount` are what reached the forwarder, so they are\n            // denominated in the bridged token, NOT in the origin token the user\n            // sent.\n            const bscUsdt = catalog?.origins.find((o) => o.chainId === BSC_CHAIN_ID);\n            const sent = formatAmount(card.sentRaw, bscUsdt?.token.decimals);\n            const sentSymbol = bscUsdt?.token.symbol ?? \"USDT\";\n            const received = formatAmount(card.receivedRaw, outDecimals);\n\n            const deposits: OrderDeposit[] = order.deposits ?? [];\n            const isOpen = Boolean(expanded[card.key]);\n            const when = formatTimestamp(order.completedAt ?? order.createdAt);\n            const statusLabel = orderStatusLabel(order.status);\n            const terminal = order.status === \"COMPLETED\" || isTerminalFailure(order.status);\n            const missingTxLabel = terminal ? \"Details unavailable\" : \"Pending\";\n            const receivedLabel = card.receivedIsEstimate\n              ? \"Estimated on ONEchain\"\n              : \"Received on ONEchain\";\n\n            return (\n              <div key={card.key} className=\"rd-history-group\">\n                <button\n                  type=\"button\"\n                  className=\"rd-history-row-button\"\n                  onClick={() => toggle(card.key)}\n                  aria-expanded={isOpen}\n                  aria-label={`Toggle transfer details: ${statusLabel}, ${when}`}\n                >\n                  <span className=\"rd-history-card-top\">\n                    <span className=\"rd-history-asset\">\n                      {card.routes > 1 ? (\n                        // Several chains funded this one receipt and the split\n                        // was impossible -- say so instead of naming whichever\n                        // deposit happened to be first.\n                        <>\n                          Multiple deposits <span className=\"rd-muted\">· {card.routes} chains</span>\n                        </>\n                      ) : (\n                        <>\n                          {origin?.token.symbol ?? outSymbol}{\" \"}\n                          <span className=\"rd-muted\">from {chainDisplayName(card.chainId)}</span>\n                        </>\n                      )}\n                    </span>\n                    <span className={statusPillClassName(order.status)} title={order.status}>\n                      {statusLabel}\n                    </span>\n                  </span>\n\n                  <span className=\"rd-history-card-amounts\">\n                    <span className=\"rd-history-amount-block\">\n                      <span className=\"rd-history-amount-label\">Processing address</span>\n                      <HistoryAmount amount={sent} symbol={sentSymbol} className=\"rd-history-num\" />\n                    </span>\n                    <span className=\"rd-history-card-arrow\" aria-hidden=\"true\">\n                      →\n                    </span>\n                    <span className=\"rd-history-amount-block\">\n                      <span className=\"rd-history-amount-label\">{receivedLabel}</span>\n                      <HistoryAmount\n                        amount={received}\n                        symbol={outSymbol}\n                        className={`rd-history-num rd-history-received${\n                          card.receivedIsEstimate ? \" rd-history-received-estimate\" : \"\"\n                        }`}\n                      />\n                    </span>\n                  </span>\n\n                  <span className=\"rd-history-card-foot\">\n                    <span className=\"rd-history-date\">\n                      {order.completedAt ? \"Completed\" : \"Started\"} · {when}\n                    </span>\n                    <span className=\"rd-history-chevron\" aria-hidden=\"true\">\n                      <ChevronDownIcon size={16} />\n                    </span>\n                  </span>\n                </button>\n\n                {isOpen && (\n                  <div className=\"rd-history-detail\">\n                    {card.deposit ? (\n                      // A split card details ITS OWN deposit leg only -- the\n                      // sibling cards carry the others.\n                      <StepRow\n                        label=\"Deposit confirmed\"\n                        // An attributed deposit links the user's ORIGIN-chain\n                        // send; an unattributed one has only its BSC arrival.\n                        chainId={card.deposit.originChainId ?? BSC_CHAIN_ID}\n                        txHash={card.deposit.originTxHash || card.deposit.txHash}\n                        amount={(() => {\n                          const a = formatAmount(card.deposit.amount, bscUsdt?.token.decimals);\n                          return a;\n                        })()}\n                      />\n                    ) : deposits.length > 0 ? (\n                      deposits.map((d) => (\n                        <StepRow\n                          key={`${d.txHash}-${d.blockNumber}`}\n                          label=\"Deposit confirmed\"\n                          chainId={d.originChainId ?? BSC_CHAIN_ID}\n                          txHash={d.originTxHash || d.txHash}\n                          amount={(() => {\n                            const a = formatAmount(d.amount, bscUsdt?.token.decimals);\n                            return a;\n                          })()}\n                        />\n                      ))\n                    ) : (\n                      // No attributed deposits is not \"no deposits\": the funds\n                      // demonstrably arrived (an order exists). Say which it is\n                      // rather than rendering an empty step.\n                      <div className=\"rd-history-step\">\n                        <span className=\"rd-history-step-dot\" />\n                        <span className=\"rd-history-step-label\">Deposit confirmed</span>\n                        <span className=\"rd-muted\">Details unavailable</span>\n                      </div>\n                    )}\n                    <StepRow\n                      label=\"Bridge\"\n                      chainId={BSC_CHAIN_ID}\n                      txHash={order.bscBridgeTx}\n                      shared={card.sharedLegs}\n                      missingLabel={missingTxLabel}\n                    />\n                    <StepRow\n                      label=\"ONEchain delivery\"\n                      chainId={catalog?.destination.chainId ?? 612055}\n                      txHash={order.deliveryTxHash}\n                      shared={card.sharedLegs}\n                      missingLabel={missingTxLabel}\n                    />\n                  </div>\n                )}\n              </div>\n            );\n          })}\n        </div>\n      )}\n    </section>\n  );\n}\n","\"use client\";\n\n// Originally ported from relay-protocol packages/relay-widget/src/components/\n// RecoverFunds.tsx (v0.1.0); REDESIGNED in place on 2026-07-30 (\"Recovery ·\n// Recover stuck funds — no addresses, action-first\" mockup), then updated to\n// upstream e5bff75 (per-forwarder sections + balance composition + up-front\n// two-transaction notice). The local \"no on-screen addresses\" decision is\n// KEPT: upstream's version prints the forwarder address and raw tx hashes; a\n// bare 0x… string is the single most alarming thing this panel could show, so\n// sections here stay address-free (tx links in the composition rows are the\n// one exception -- they are the user's own sends, rendered as short links the\n// same way the history modal renders them).\n//\n// The panel is organized PER FORWARDER, not per delivery token. A deployment's\n// targets do not map one-to-one onto forwarders: a depositor-kind target (e.g.\n// pONEUSD) derives its own, while every swap-kind target shares one. Laying\n// out sections per target would put two Recover buttons on a single balance\n// the moment two swap-kind targets exist -- both moving the whole balance, the\n// second reverting -- the same hazard that rules out splitting per ORIGIN.\n// See ../lib/recoveryGroups.ts.\n//\n// Non-custody invariant, unchanged: the USER signs the permissionless recovery\n// tx (they pay BSC gas), and every recovery tx (factory.deploy,\n// forwarder.execute/sweepToUser) carries no destination argument -- funds\n// always route to the forwarder's immutable recipient. `info.inFlight` is\n// advisory only, never used to disable a button.\nimport { useEffect, useState } from \"react\";\n\nimport { formatUnits } from \"../lib/units\";\nimport type { RelayClient } from \"../lib/client\";\nimport type { OrderSummary, RecoveryInfo } from \"../lib/types\";\nimport type { RecoverySuccessResult, RelayWalletProps } from \"../types\";\nimport type { RecoveryAction, UseRecoveryOptions } from \"../hooks/useRecovery\";\nimport { useRecovery, useRecoveryActions } from \"../hooks/useRecovery\";\nimport type { ForwarderGroup, GroupedDeposit, RecoveryTargetEntry } from \"../lib/recoveryGroups\";\nimport { groupForwarders, groupsWithFunds } from \"../lib/recoveryGroups\";\nimport { chainDisplayName, explorerTxLink } from \"../lib/explorer\";\nimport { TokenIcon } from \"./icons\";\nimport \"../styles/component.css\";\n\n/** BSC -- every forwarder lives here, so an unattributed deposit's arrival tx\n * is a BSC one. */\nconst BSC_CHAIN_ID = 56;\n\nexport interface RecoverFundsProps {\n  /** Pre-built client. Takes precedence over apiBaseUrl. */\n  client?: RelayClient;\n  /** Used to build a client via createRelayClient when `client` isn't passed. */\n  apiBaseUrl?: string;\n  /** The EVM address to check/recover for -- normally the connected wallet's own address. */\n  recipient?: string;\n  /**\n   * The currently selected CROSS delivery target symbol. Only used when\n   * `targets` is NOT supplied, i.e. when this panel fetches for itself: it then\n   * reports the one forwarder that target derives. Omitted/undefined selects\n   * the configured default.\n   */\n  target?: string;\n  /**\n   * Recovery info for EVERY delivery target, pre-fetched by the caller --\n   * normally `<RelayDeposit>`/`<RelayRecovery>`, which already make exactly\n   * these per-target `getRecovery` calls for the trigger badge. Supplying it\n   * puts this panel in per-forwarder mode: it groups the entries by forwarder\n   * address and renders a section for each one holding funds, and does NOT\n   * fetch anything itself (that would duplicate the caller's request, for one\n   * target only).\n   *\n   * Omitted: the panel fetches the single `target` for itself, exactly as before.\n   */\n  targets?: readonly RecoveryTargetEntry[];\n  /** Loading state for caller-supplied `targets`; ignored by the fetch owner. */\n  targetsLoading?: boolean;\n  /**\n   * The user's orders, used read-only to show WHICH deposits make up each\n   * forwarder's balance (origin chain + the user's own send). Only the\n   * BSC_FUNDED ones can explain a live balance -- see recoveryGroups.ts.\n   * Omitted: balances render without a provenance breakdown.\n   */\n  orders?: readonly OrderSummary[];\n  /**\n   * Called after a recovery attempt changes on-chain state, so the owner of\n   * `targets` can refetch it. Only meaningful in per-forwarder mode; when this\n   * panel fetches for itself it refreshes its own data.\n   */\n  onRecovered?: () => void;\n  /** Called once after the recovery action receipt confirms successfully. */\n  onRecoverySuccess?: (result: RecoverySuccessResult) => void;\n  /**\n   * Injected wallet capabilities (dapp-ui depends on neither wagmi nor viem, so the host\n   * supplies these) -- passed straight through to the recovery write path, which\n   * signs/switches/reads on BSC. Omitted degrades exactly like a missing capability there:\n   * `recover()` fails closed rather than crashing the panel.\n   */\n  wallet?: RelayWalletProps;\n  /**\n   * Count of recoverable tokens under OTHER (non-selected) catalog targets.\n   *\n   * Retained for callers driving this panel one target at a time with their own\n   * delivery-token picker. `<RelayDeposit>` no longer passes it and no longer\n   * needs to: in per-forwarder mode nothing is hidden behind a selection, so\n   * there is no \"switch the delivery token to find the rest\" left to say.\n   */\n  otherTargetsRecoverable?: number;\n  /**\n   * Overrides the pinned factory allowlist `recover()` verifies against before signing\n   * (security report L-003/L-008) -- only needed by a self-hosted deployment running its\n   * own factories. Defaults to DEFAULT_TRUSTED_FACTORIES.\n   */\n  trustedFactories?: readonly string[];\n  /**\n   * Render this panel as its own framed \"Recover stuck funds\" section (default\n   * `true`). `<RelayRecovery>` passes `false`: the modal shell already supplies\n   * the accessible title and surface, so another outer card would be redundant.\n   */\n  showTitle?: boolean;\n  /** Called on a 401 from GET /v1/recovery. */\n  onUnauthorized?: () => void;\n  onError?: (error: Error) => void;\n}\n\nfunction propsToOpts(props: RecoverFundsProps): UseRecoveryOptions {\n  return {\n    client: props.client,\n    apiBaseUrl: props.apiBaseUrl,\n    recipient: props.recipient,\n    target: props.target,\n    // Supplying `targets` means the caller already fetched every target's info;\n    // fetching again here would re-request what it just got.\n    enabled: props.targets === undefined,\n    wallet: props.wallet,\n    trustedFactories: props.trustedFactories,\n    onUnauthorized: props.onUnauthorized,\n    onError: props.onError,\n  };\n}\n\nconst DEFAULT_IN_FLIGHT_REASON = \"This balance may already be processing. Recover only if it remains stuck.\";\n\n/** The legacy top-level `balance`/`decimals` pair only ever reports BSC USDT (see RecoveryInfo's\n * doc), so that's the symbol its row shows when the backend predates `tokens[]`. */\nconst LEGACY_SYMBOL = \"USDT\";\n\n/** Row key for the row built from the legacy top-level `balance` field -- a fixed sentinel rather\n * than an address, since that row's action deliberately passes NO token address (back-compat). */\nconst PRIMARY_ROW_KEY = \"primary\";\n\n/** Concise, non-alarming note pointing at the delivery-token picker when a\n * single-target caller's aggregate is higher than what it lists. Singular/plural\n * agree with `n`. */\nfunction otherTargetsNote(n: number): string {\n  return `${n} more recoverable under other delivery tokens -- switch the delivery token above to recover ${\n    n === 1 ? \"it\" : \"them\"\n  }.`;\n}\n\n/** Keep provider/viem diagnostics out of the primary error line. They can be\n * several paragraphs long and expose calldata that does not help a user decide\n * what to do next; the untouched text remains available in the disclosure. */\nfunction recoveryErrorMessage(error: string): string {\n  const normalized = error.toLowerCase();\n\n  if (\n    normalized.includes(\"insufficient funds\") ||\n    normalized.includes(\"insufficientfunds\") ||\n    normalized.includes(\"exceeds the balance of the account\") ||\n    normalized.includes(\"insufficient balance for transfer\")\n  ) {\n    return \"Not enough BNB for the network fee. Add BNB to your connected wallet and try again.\";\n  }\n\n  if (\n    normalized.includes(\"user rejected\") ||\n    normalized.includes(\"userrejected\") ||\n    normalized.includes(\"user denied\") ||\n    normalized.includes(\"user cancelled\") ||\n    normalized.includes(\"user canceled\") ||\n    normalized.includes(\"action_rejected\") ||\n    normalized.includes(\"rejected by user\")\n  ) {\n    return \"Transaction cancelled in your wallet.\";\n  }\n\n  return \"Recovery couldn't be completed. Check your wallet and try again.\";\n}\n\n/** Developer-facing diagnostics live outside the red alert so opening them\n * does not turn the whole error state into one oversized danger panel. */\nfunction TechnicalDetails({ message }: { message: string }) {\n  const [copyStatus, setCopyStatus] = useState<\"idle\" | \"copied\" | \"failed\">(\"idle\");\n\n  useEffect(() => {\n    if (copyStatus === \"idle\") return;\n    const timer = window.setTimeout(() => setCopyStatus(\"idle\"), 1500);\n    return () => window.clearTimeout(timer);\n  }, [copyStatus]);\n\n  async function copyDetails() {\n    if (typeof navigator === \"undefined\" || !navigator.clipboard) {\n      setCopyStatus(\"failed\");\n      return;\n    }\n\n    try {\n      await navigator.clipboard.writeText(message);\n      setCopyStatus(\"copied\");\n    } catch {\n      setCopyStatus(\"failed\");\n    }\n  }\n\n  const copyLabel =\n    copyStatus === \"copied\"\n      ? \"Copied technical details\"\n      : copyStatus === \"failed\"\n        ? \"Copy failed\"\n        : \"Copy technical details\";\n\n  return (\n    <details className=\"rd-error-details\">\n      <summary>View technical details</summary>\n      <div className=\"rd-error-log\">\n        <button\n          type=\"button\"\n          className=\"rd-error-copy\"\n          onClick={() => void copyDetails()}\n          aria-label={copyLabel}\n          aria-live=\"polite\"\n          title={copyLabel}\n        >\n          {copyStatus === \"copied\" ? (\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n              <path\n                d=\"m5 12 4 4L19 6\"\n                stroke=\"currentColor\"\n                strokeWidth=\"2.2\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n              />\n            </svg>\n          ) : (\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n              <rect x=\"8\" y=\"8\" width=\"11\" height=\"11\" rx=\"2\" stroke=\"currentColor\" strokeWidth=\"1.8\" />\n              <path\n                d=\"M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.8\"\n                strokeLinecap=\"round\"\n              />\n            </svg>\n          )}\n        </button>\n        <code>{message}</code>\n      </div>\n    </details>\n  );\n}\n\n/** Every smallest-unit amount off the API is a base-10 string; a malformed one degrades to 0n\n * rather than throwing the whole panel away. All comparisons/arithmetic below stay in BigInt. */\nfunction toBigInt(value: string | undefined): bigint {\n  if (!value) return 0n;\n  try {\n    return BigInt(value);\n  } catch {\n    return 0n;\n  }\n}\n\n/** Human-readable amount (floor semantics -- formatUnits is a pure digit shift). Display only:\n * never fed back into any computation. */\nfunction human(value: bigint, decimals: number): string {\n  try {\n    return formatUnits(value, decimals);\n  } catch {\n    return value.toString();\n  }\n}\n\n/** Truncates a 0x hash for inline display. Defensive fallback for short strings. */\nfunction shortHash(hash: string): string {\n  return hash.length > 14 ? `${hash.slice(0, 8)}...${hash.slice(-4)}` : hash;\n}\n\n/**\n * One on-screen asset row. Derived entirely from `RecoveryInfo` -- `action` is the recover()\n * call this row's button makes (undefined = nothing this row can do on its own), and `shortfall`\n * is how much more of the same asset the bridge minimum still needs.\n */\ninterface RecoveryRow {\n  key: string;\n  symbol: string;\n  decimals: number;\n  balance: bigint;\n  action?: { kind: RecoveryAction; tokenAddress?: string };\n  /** Set only when the row is NOT recoverable because it sits below the bridge minimum. */\n  shortfall?: bigint;\n  /** The bridge minimum, in this row's own units -- only meaningful alongside `shortfall`. */\n  minimum?: bigint;\n}\n\n/**\n * Builds one forwarder's row list, newest semantics first:\n *\n *   * `tokens[]` (Task 6/7 backend) is the source of truth when present -- one row per token\n *     holding a nonzero balance, INCLUDING the ones the server flags un-recoverable (those are\n *     exactly the below-minimum leftovers the redesign has to explain rather than hide);\n *   * a top-level `suggestedAction: \"execute\"` still gets its own row when no `tokens[]` entry\n *     already carries the execute action -- that's the normal bridge-completion asset;\n *   * with no `tokens[]` at all (older backend) the legacy `balance`/`decimals` pair is the only\n *     row, and its action passes no token address -- byte-identical to pre-Task-7 behavior.\n *\n * Zero-balance rows are omitted everywhere: there is nothing to say about them.\n */\nfunction buildRows(info: RecoveryInfo): RecoveryRow[] {\n  const minimum = toBigInt(info.bridgeMinWei);\n  const rows: RecoveryRow[] = [];\n\n  for (const token of info.tokens ?? []) {\n    const balance = toBigInt(token.balance);\n    if (balance <= 0n) continue;\n    if (token.suggestedAction === \"execute\") {\n      rows.push({\n        key: token.address,\n        symbol: token.symbol,\n        decimals: token.decimals,\n        balance,\n        action: { kind: \"execute\" },\n      });\n      continue;\n    }\n    if (token.recoverable) {\n      rows.push({\n        key: token.address,\n        symbol: token.symbol,\n        decimals: token.decimals,\n        balance,\n        action: { kind: \"sweep\", tokenAddress: token.address },\n      });\n      continue;\n    }\n    rows.push({\n      key: token.address,\n      symbol: token.symbol,\n      decimals: token.decimals,\n      balance,\n      ...(minimum > balance ? { shortfall: minimum - balance, minimum } : {}),\n    });\n  }\n\n  const primaryBalance = toBigInt(info.balance);\n  const hasExecuteRow = rows.some((r) => r.action?.kind === \"execute\");\n  if (primaryBalance > 0n && info.suggestedAction === \"execute\" && !hasExecuteRow) {\n    // The bridge-completion asset the top-level fields describe. Prepended: it is the asset the\n    // user actually deposited, so it leads the list ahead of any misdeposited leftovers.\n    rows.unshift({\n      key: PRIMARY_ROW_KEY,\n      symbol: LEGACY_SYMBOL,\n      decimals: info.decimals,\n      balance: primaryBalance,\n      action: { kind: \"execute\" },\n    });\n  } else if (rows.length === 0 && primaryBalance > 0n && info.suggestedAction !== \"none\") {\n    // Older backend with no `tokens[]`: the legacy single-asset row. `sweep` with no token\n    // address, exactly as the pre-Task-7 generic button did.\n    rows.push({\n      key: PRIMARY_ROW_KEY,\n      symbol: LEGACY_SYMBOL,\n      decimals: info.decimals,\n      balance: primaryBalance,\n      action: { kind: \"sweep\" },\n    });\n  }\n\n  return rows;\n}\n\n/** One provenance row: which chain a deposit came from, how much arrived, and a\n * link to the user's own send. Read-only by design -- there is no per-origin\n * action to offer (see ForwarderGroup.deposits). */\nfunction DepositRow({ deposit, decimals }: { deposit: GroupedDeposit; decimals: number }) {\n  // An attributed deposit links the user's ORIGIN-chain send; an unattributed one\n  // has only its BSC arrival, which is still worth linking -- and is permanently\n  // correct for a direct BSC deposit, which has no cross-chain leg at all.\n  const chainId = deposit.originChainId ?? BSC_CHAIN_ID;\n  const txHash = deposit.originTxHash || deposit.txHash;\n  const link = txHash ? explorerTxLink(chainId, txHash) : undefined;\n  return (\n    <div className=\"rd-history-step\" data-testid=\"recovery-deposit-row\">\n      <span className=\"rd-history-step-dot rd-history-step-dot-done\" />\n      <span className=\"rd-history-step-chain\">{chainDisplayName(chainId)}</span>\n      <span className=\"rd-history-step-amount\">{human(toBigInt(deposit.amount), decimals)}</span>\n      <span className=\"rd-history-step-tx\">\n        {link ? (\n          <a href={link.url} target=\"_blank\" rel=\"noreferrer\" title={txHash}>\n            {shortHash(txHash)}\n          </a>\n        ) : txHash ? (\n          <span title={txHash}>{shortHash(txHash)}</span>\n        ) : null}\n      </span>\n    </div>\n  );\n}\n\ninterface ForwarderSectionProps {\n  group: ForwarderGroup;\n  /** Whether to name which delivery tokens this forwarder serves. Worth saying in\n   * two cases: several forwarders are listed (so the user can tell which is\n   * which), or this one serves several targets (so a user who picked one of them\n   * understands why a single entry covers it). Suppressed only in the plain\n   * one-forwarder-one-target case, where it would be noise. */\n  showTargetLabel: boolean;\n  busy: boolean;\n  state: ReturnType<ReturnType<typeof useRecoveryActions>[\"stateFor\"]>;\n  onRecover: (row: RecoveryRow) => void;\n  /** Which row's button was clicked (panel-scoped key), so the in-progress\n   * spinner replaces THAT row's button only. */\n  actingRowKey?: string;\n  rowKeyOf: (row: RecoveryRow) => string;\n}\n\nfunction ForwarderSection({\n  group,\n  showTargetLabel,\n  busy,\n  state,\n  onRecover,\n  actingRowKey,\n  rowKeyOf,\n}: ForwarderSectionProps) {\n  const { info } = group;\n  const rows = buildRows(info);\n  const balanceValue = toBigInt(info.balance);\n\n  /** Never sums across symbols: a single row shows its own amount, several show a count. */\n  const headline = rows.length === 1 ? `${human(rows[0].balance, rows[0].decimals)}` : `${rows.length} assets`;\n\n  // The listed deposits can add up to LESS than the balance: a deposit seen\n  // on-chain but not yet swept into an order has no order to be listed under,\n  // and residual dust from an order already written off has none either.\n  // Saying so is the whole difference between a breakdown and a claim.\n  const depositsExplainAll = group.deposits.length > 0 && group.depositsTotal >= balanceValue;\n  const unexplained = balanceValue > group.depositsTotal ? balanceValue - group.depositsTotal : 0n;\n\n  const running = state.step === \"switching\" || state.step === \"deploying\" || state.step === \"recovering\";\n  const actingRow = rows.find((row) => rowKeyOf(row) === actingRowKey);\n  const actingAction = actingRow?.action?.kind;\n\n  /** The wallet-confirmation progress for the acting row, in the user's terms. */\n  function stepText(): string {\n    if (state.step === \"switching\") return \"Switching to BNB Chain…\";\n    if (state.step === \"deploying\") return \"Confirm in wallet… (1/2 — preparing your deposit address)\";\n    const action = actingAction === \"execute\" ? \"continuing ONEchain delivery\" : \"returning funds\";\n    return state.deployTxHash ? `Confirm in wallet… (2/2 — ${action})` : `Confirm in wallet… (${action})`;\n  }\n\n  return (\n    <div className=\"rd-recover-group\" data-testid=\"forwarder-section\">\n      {showTargetLabel && (\n        <p className=\"rd-recover-group-label\">\n          {/* Plural because several delivery tokens genuinely share one forwarder\n              (every swap-kind target does) -- and when they do, this is ONE\n              balance, recovered once, not one per token. */}\n          Delivery target · <b>{group.targets.join(\", \")}</b>\n        </p>\n      )}\n\n      <div className=\"rd-recover-sum\">\n        <span className=\"rd-recover-sum-label\">Funds awaiting processing</span>\n        <span\n          className=\"rd-recover-sum-value\"\n          title={rows.length === 1 ? `${headline} ${rows[0].symbol}` : headline}\n        >\n          {headline}\n          {rows.length === 1 && (\n            <>\n              {\" \"}\n              <span className=\"rd-recover-sum-unit\">{rows[0].symbol}</span>\n            </>\n          )}\n        </span>\n      </div>\n\n      {group.deposits.length > 0 && (\n        <details className=\"rd-recover-made-of\">\n          <summary className=\"rd-recover-made-of-label\">\n            Deposit history · {group.deposits.length} {group.deposits.length === 1 ? \"transfer\" : \"transfers\"}\n          </summary>\n          <div className=\"rd-recover-made-of-body\">\n            {group.deposits.map((d) => (\n              <DepositRow key={`${d.txHash}-${d.blockNumber}`} deposit={d} decimals={info.decimals} />\n            ))}\n            {!depositsExplainAll && (\n              <p className=\"rd-token-row-status\" data-testid=\"deposits-incomplete-note\">\n                {/* Named precisely: the rows above are what we can ATTRIBUTE, and the\n                    gap is real money that a recovery will also move. */}\n                These transfers explain {human(group.depositsTotal, info.decimals)} of{\" \"}\n                {human(balanceValue, info.decimals)} {LEGACY_SYMBOL}. The remaining{\" \"}\n                {human(unexplained, info.decimals)} {LEGACY_SYMBOL} is still included when you act.\n              </p>\n            )}\n          </div>\n        </details>\n      )}\n\n      <div className=\"rd-token-list\">\n        {rows.map((row) => {\n          const key = rowKeyOf(row);\n          const acting = running && actingRowKey === key;\n          return (\n            <div key={row.key} className=\"rd-token-row\" data-token-symbol={row.symbol}>\n              <TokenIcon symbol={row.symbol} size={26} />\n              <div className=\"rd-token-row-info\">\n                {rows.length > 1 && (\n                  <span className=\"rd-token-row-amount\">\n                    {human(row.balance, row.decimals)} <span className=\"rd-token-row-symbol\">{row.symbol}</span>\n                  </span>\n                )}\n                {acting ? (\n                  <span className=\"rd-token-row-step\" role=\"status\" aria-live=\"polite\">\n                    {stepText()}\n                  </span>\n                ) : row.shortfall !== undefined && row.minimum !== undefined ? (\n                  <span className=\"rd-token-row-warn\">\n                    Needs <b>{human(row.shortfall, row.decimals)} {row.symbol}</b> more to reach the{\" \"}\n                    {human(row.minimum, row.decimals)} {row.symbol} minimum. Add it through{\" \"}\n                    <b>Deposit to CROSS</b> and both amounts will process together.\n                  </span>\n                ) : row.action ? (\n                  <span className=\"rd-token-row-status\">\n                    {row.action.kind === \"execute\"\n                      ? \"Ready to continue delivery to ONEchain\"\n                      : \"Ready to return to your BNB Chain wallet\"}\n                  </span>\n                ) : (\n                  <span className=\"rd-token-row-status\">Waiting for processing</span>\n                )}\n              </div>\n              {acting ? (\n                <span className=\"rd-spinner rd-token-row-spinner\" />\n              ) : (\n                row.action && (\n                  <button\n                    type=\"button\"\n                    className=\"rd-button-primary rd-token-row-action\"\n                    onClick={() => onRecover(row)}\n                    disabled={busy}\n                  >\n                    {row.action.kind === \"execute\" ? \"Continue delivery\" : \"Return to wallet\"}\n                  </button>\n                )\n              )}\n            </div>\n          );\n        })}\n      </div>\n\n      {/* The two-transaction case surprised a user, so it is stated BEFORE they\n          commit rather than discovered at the second wallet prompt (upstream\n          e5bff75). An ERC-20 balance can sit at a CREATE2 address with no code,\n          but sweepToUser is a contract function, so the forwarder has to exist\n          first. */}\n      {!info.isDeployed && state.step === \"idle\" && (\n        <div className=\"rd-callout rd-callout-warning\" data-testid=\"two-tx-notice\">\n          The first action from this address needs <b>two wallet confirmations</b>: prepare the address,\n          then move the funds. Later actions need one.\n        </div>\n      )}\n\n      {/* Advisory only, never a blocker (see this file's header). */}\n      {info.inFlight && (\n        <p className=\"rd-muted rd-token-row-status\">{info.inFlightReason || DEFAULT_IN_FLIGHT_REASON}</p>\n      )}\n\n      {state.step === \"done\" && (\n        <div className=\"rd-callout rd-callout-success\" role=\"status\" aria-live=\"polite\">\n          {actingAction === \"execute\"\n            ? \"ONEchain delivery restarted.\"\n            : \"Funds returned to your connected BNB Chain wallet.\"}\n        </div>\n      )}\n\n      {state.step === \"error\" && state.error && (\n        <>\n          <div className=\"rd-callout rd-callout-danger\" role=\"alert\">\n            {recoveryErrorMessage(state.error)}\n          </div>\n          <TechnicalDetails message={state.error} />\n        </>\n      )}\n    </div>\n  );\n}\n\nexport function RecoverFunds(props: RecoverFundsProps) {\n  // Own fetch, used only when the caller did not supply `targets` (see\n  // propsToOpts' `enabled`). Its `recover`/`recoveryState` are not used here --\n  // the sections drive the shared write path below so each forwarder gets its\n  // own button and its own progress.\n  const { info, loading, error, refresh } = useRecovery(propsToOpts(props));\n  const isLoading = loading || (props.targets !== undefined && props.targetsLoading === true);\n\n  const actions = useRecoveryActions({\n    recipient: props.recipient,\n    wallet: props.wallet,\n    trustedFactories: props.trustedFactories,\n    onError: props.onError,\n    onRecoverySuccess: props.onRecoverySuccess,\n    // Refresh whichever side owns the data: the caller's per-target fetch in\n    // per-forwarder mode, this panel's own otherwise.\n    onChanged: props.targets !== undefined ? props.onRecovered : refresh,\n  });\n\n  /** Which (forwarder, row) button was clicked, so the in-progress spinner + step\n   * text replace THAT row's button (and only that one). Presentation state only. */\n  const [actingRowKey, setActingRowKey] = useState<string>();\n\n  // Entries to group. In per-forwarder mode they come from the caller; otherwise\n  // this panel's single fetched target stands alone. `isDefault` matters only for\n  // matching orders (which report \"\" for the default target), and a lone\n  // self-fetched entry is treated as the default so an order with target \"\" still\n  // matches it.\n  const entries: RecoveryTargetEntry[] =\n    props.targets !== undefined\n      ? [...props.targets]\n      : info\n        ? [{ symbol: props.target ?? \"\", isDefault: true, info }]\n        : [];\n\n  const groups = groupsWithFunds(groupForwarders(entries, props.orders ?? []));\n  const hasFunds = groups.length > 0;\n  const refreshFunds = props.targets !== undefined ? props.onRecovered : refresh;\n\n  // The CONNECTED wallet's BNB balance -- recovery txs run on BSC and the gas\n  // comes from the signer (walletAddress), not the recipient. Fetched only\n  // when there is something to recover and the host injected `getBalance`;\n  // failure (or a missing capability) just leaves the note without a figure.\n  // \"Insufficient funds\" at the wallet prompt was this panel's most confusing\n  // dead end -- the user sees USDT waiting and no reason a click should fail.\n  const walletAddress = props.wallet?.walletAddress;\n  const getBalance = props.wallet?.getBalance;\n  const [gasBalance, setGasBalance] = useState<{ value: bigint; decimals: number } | undefined>();\n  useEffect(() => {\n    if (!hasFunds || !getBalance || !walletAddress) {\n      setGasBalance(undefined);\n      return;\n    }\n    let cancelled = false;\n    getBalance({ chainId: 56, address: walletAddress as `0x${string}` })\n      .then((b) => {\n        if (!cancelled) setGasBalance({ value: b.value, decimals: b.decimals });\n      })\n      .catch(() => {\n        if (!cancelled) setGasBalance(undefined);\n      });\n    return () => {\n      cancelled = true;\n    };\n  }, [hasFunds, getBalance, walletAddress]);\n\n  return (\n    <section\n      className={(props.showTitle ?? true) ? \"rd-card\" : \"rd-stack\"}\n      data-rd-recover-funds=\"\"\n    >\n      {(props.showTitle ?? true) && (\n        <div className=\"rd-card-header\">\n          <h2 className=\"rd-card-title\">Recover stuck funds</h2>\n        </div>\n      )}\n\n      {/* Only meaningful for single-target embedders -- <RelayDeposit>/<RelayRecovery>\n          run per-forwarder mode and no longer pass it (nothing is hidden behind the\n          selection any more). */}\n      {typeof props.otherTargetsRecoverable === \"number\" && props.otherTargetsRecoverable > 0 && (\n        <div className=\"rd-callout rd-callout-muted\" data-testid=\"other-targets-recoverable-note\">\n          {otherTargetsNote(props.otherTargetsRecoverable)}\n        </div>\n      )}\n\n      {isLoading && (\n        <p className=\"rd-muted\" role=\"status\" aria-live=\"polite\">\n          <span className=\"rd-spinner\" /> Checking for recoverable funds…\n        </p>\n      )}\n\n      {error && (\n        <>\n          <div className=\"rd-callout rd-callout-danger\" role=\"alert\">\n            Couldn&apos;t check recoverable funds. Please try again.\n          </div>\n          <TechnicalDetails message={error.message} />\n        </>\n      )}\n\n      {!isLoading && !error && !hasFunds && (\n        <div className=\"rd-recover-empty\" role=\"status\" data-testid=\"rd-recover-empty-state\">\n          <span className=\"rd-recover-empty-icon\" aria-hidden=\"true\">\n            <svg width=\"34\" height=\"34\" viewBox=\"0 0 24 24\" fill=\"none\" focusable=\"false\">\n              <path\n                d=\"M5 11.5V8.8A2.8 2.8 0 0 1 7.8 6h8.4A2.8 2.8 0 0 1 19 8.8v6.4a2.8 2.8 0 0 1-2.8 2.8H7.8A2.8 2.8 0 0 1 5 15.2v-3.7Z\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.7\"\n              />\n              <path d=\"M5 10h14\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n              <path\n                d=\"m9.2 14 1.7 1.7 3.9-4\"\n                stroke=\"currentColor\"\n                strokeWidth=\"1.8\"\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n              />\n            </svg>\n          </span>\n          <h3 className=\"rd-recover-sum-value rd-recover-sum-empty rd-recover-empty-title\">\n            Nothing to recover\n          </h3>\n          <p className=\"rd-recover-empty-copy\">\n            No funds are waiting at your deposit addresses. If funds become stuck, they will appear here.\n          </p>\n          {refreshFunds && (\n            <button\n              type=\"button\"\n              className=\"rd-button-secondary rd-recover-empty-refresh\"\n              onClick={refreshFunds}\n              data-track=\"refresh_recovery\"\n            >\n              <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n                <path\n                  d=\"M20 7v5h-5M4 17v-5h5M6.1 9A7 7 0 0 1 18.6 7M5.4 17A7 7 0 0 0 17.9 15\"\n                  stroke=\"currentColor\"\n                  strokeWidth=\"2\"\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                />\n              </svg>\n              Check again\n            </button>\n          )}\n        </div>\n      )}\n\n      {!isLoading &&\n        !error &&\n        groups.map((group) => (\n          <ForwarderSection\n            key={group.forwarder.toLowerCase()}\n            group={group}\n            showTargetLabel={groups.length > 1 || group.targets.length > 1}\n            busy={actions.busy}\n            state={actions.stateFor(group.forwarder)}\n            actingRowKey={actingRowKey}\n            rowKeyOf={(row) => `${group.forwarder.toLowerCase()}:${row.key}`}\n            onRecover={(row) => {\n              if (!row.action) return;\n              setActingRowKey(`${group.forwarder.toLowerCase()}:${row.key}`);\n              void actions.recover(group.info, row.action.kind, row.action.tokenAddress);\n            }}\n          />\n        ))}\n\n      {/* Recovery signs on BSC and the gas comes out of the CONNECTED wallet's\n          BNB -- said up front, with the wallet's live balance when the host can\n          read it, because \"insufficient funds\" at the wallet prompt is this\n          panel's most confusing dead end otherwise. */}\n      {!isLoading && !error && hasFunds && (\n        <div className=\"rd-callout rd-callout-muted\" data-testid=\"rd-recover-gas-note\">\n          You submit this transaction from your connected wallet and pay the BNB Chain network fee in BNB.\n          Available balance:\n          {gasBalance && (\n            <>\n              {\" \"}\n              <b>{human(gasBalance.value, gasBalance.decimals)} BNB</b>\n            </>\n          )}\n          {!gasBalance && \" unavailable\"}.\n        </div>\n      )}\n\n      {/* The one thing worth saying about WHERE the money goes -- it replaces the forwarder\n          address this panel used to print. True by construction: no recovery tx takes a\n          destination argument. */}\n      {!isLoading && !error && hasFunds && (\n        <p className=\"rd-recover-foot\">\n          Delivery continues to ONEchain; returned funds go to your connected BNB Chain wallet.\n        </p>\n      )}\n    </section>\n  );\n}\n","// Ported from relay-protocol packages/relay-widget/src/useRecovery.ts\n// (v0.1.0, updated to upstream e5bff75). Mechanical substitution only: the\n// wagmi hooks the original consumed (useAccount, useSwitchChain,\n// useWriteContract, usePublicClient) are replaced by the injected\n// `opts.wallet` functions (RelayWalletProps), and viem's `isAddress` by\n// ../lib/units -- dapp-ui may depend on neither wagmi nor viem.\n// verifyForwarder's signature grew a leading (readContract, chainId) pair\n// instead of taking a wagmi publicClient. Everything else is the original\n// logic, untouched.\n//\n// e5bff75 split the WRITE path out into `useRecoveryActions`, which takes the\n// forwarder's info as an ARGUMENT and keys progress by forwarder address --\n// because a hook cannot be called once per forwarder in a loop, and the\n// recovery panel now renders one section per forwarder (a depositor-kind\n// target derives its own forwarder; every swap-kind target shares one -- see\n// ../lib/recoveryGroups.ts). `useRecovery` is unchanged for its callers: it is\n// now that hook plus a fetch, binding its own single fetched `info` in.\n//\n// Every recovery tx (factory.deploy, forwarder.execute/sweepToUser) carries\n// no destination argument: funds always route to the forwarder's immutable\n// recipient, so `info` here is advisory/orientation only, never a gate the\n// UI uses to block an action. `info.inFlight` is shown as a caution line but\n// never disables a button.\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { createRelayClient, RelayApiError } from \"../lib/client\";\nimport type { RelayClient } from \"../lib/client\";\nimport type { RecoveryInfo } from \"../lib/types\";\nimport { standingForwarderAbi, standingForwarderFactoryAbi } from \"../lib/abis\";\nimport { verifyForwarder } from \"../lib/verifyForwarder\";\nimport { isAddress } from \"../lib/units\";\nimport type { Hex, RecoverySuccessResult, RelayWalletProps } from \"../types\";\n\n/** Every recovery tx (factory.deploy, forwarder.execute/sweepToUser) is signed and sent on\n * BSC, regardless of which chain the connected wallet started on. */\nconst BSC_CHAIN_ID = 56;\n\nexport type RecoveryAction = \"execute\" | \"sweep\";\n\n/** Per-attempt progress for `recover()`. \"switching\" = prompting a chain switch to BSC;\n * \"deploying\" = the factory.deploy() write is in flight/confirming (only needed when the\n * forwarder wasn't deployed yet); \"recovering\" = the execute()/sweepToUser() write is in\n * flight/confirming; \"done\"/\"error\" are terminal for this attempt. */\nexport type RecoveryStep = \"idle\" | \"switching\" | \"deploying\" | \"recovering\" | \"done\" | \"error\";\n\nexport interface RecoveryState {\n  step: RecoveryStep;\n  /** Tx hash of the factory.deploy() write -- only set when this attempt needed one. */\n  deployTxHash?: string;\n  /** Tx hash of the execute()/sweepToUser() write. */\n  actionTxHash?: string;\n  error?: string;\n}\n\nconst IDLE_STATE: RecoveryState = { step: \"idle\" };\n\n/**\n * Which token a token-less `recover('sweep')` call sweeps (Task 7 back-compat default): the\n * first `info.tokens` entry the server flags `recoverable`, or -- when `tokens` is absent\n * (older backend, or none recoverable) -- the legacy `info.token` (BSC USDT) that pre-Task-6\n * callers always swept. Purely a WHICH-ASSET selection; never affects the recovery\n * destination (see `recover`'s doc below).\n */\nfunction primaryRecoverableToken(info: RecoveryInfo): string {\n  return info.tokens?.find((t) => t.recoverable)?.address ?? info.token;\n}\n\nexport interface UseRecoveryActionsOptions {\n  /** The EVM address recovery actions are for -- normally the connected wallet's own\n   * address. Funds always route here (the forwarder's immutable recipient), never anywhere\n   * else, regardless of which action is taken. */\n  recipient?: string;\n  /** Injected wallet capabilities (dapp-ui depends on neither wagmi nor viem). A missing\n   * writeContract/readContract/waitForReceipt fails `recover()` closed rather than\n   * crashing; a missing switchChain only errors when a switch is actually needed. */\n  wallet?: RelayWalletProps;\n  /** Overrides the pinned set of factories `recover()` will sign against\n   * (security report L-003/L-008) -- only needed by a self-hosted deployment\n   * running its own factories. Defaults to DEFAULT_TRUSTED_FACTORIES.\n   * Deliberately NOT sourced from the API (see verifyForwarder.ts). */\n  trustedFactories?: readonly string[];\n  onError?: (error: Error) => void;\n  /** Called once after the action receipt confirms successfully. */\n  onRecoverySuccess?: (result: RecoverySuccessResult) => void;\n  /** Called after a recovery attempt changes on-chain state (a completed deploy, a\n   * completed action), so the owner of the recovery data can refetch it. */\n  onChanged?: () => void;\n}\n\nexport interface UseRecoveryActionsResult {\n  /**\n   * Signs the requested recovery tx for ONE specific forwarder, taking that\n   * forwarder's info explicitly rather than closing over a single selected one.\n   *\n   * That signature is what lets the panel render a section per forwarder: a\n   * deployment's targets can resolve to several distinct forwarders (a\n   * depositor-kind target derives its own), each holding its own balance and\n   * needing its own button, and a hook cannot be called once per forwarder in a\n   * loop. So the WRITE path takes the info as an argument and the state is keyed\n   * by forwarder -- see stateFor.\n   *\n   * `tokenAddress` selects WHICH asset a \"sweep\" recovers -- passed straight\n   * through as sweepToUser's sole argument. It does NOT affect where funds go:\n   * every recovery tx carries no destination argument and always routes to the\n   * forwarder's immutable recipient. Omitted: defaults to the primary\n   * recoverable token from `info.tokens`, falling back to the legacy\n   * `info.token` (BSC USDT).\n   */\n  recover: (info: RecoveryInfo, action: RecoveryAction, tokenAddress?: string) => Promise<void>;\n  /** This forwarder's own attempt progress, so one section's spinner and error\n   * never appear under another's balance. Idle for a forwarder never acted on. */\n  stateFor: (forwarder: string | undefined) => RecoveryState;\n  /** True while ANY forwarder's attempt is in flight. Buttons are disabled\n   * globally during one: the wallet can only work on one transaction at a time,\n   * so offering a second is offering a queue the user did not ask for. */\n  busy: boolean;\n}\n\n/**\n * The write half of the recovery flow: chain switch, pre-signing address\n * verification, optional factory.deploy, then execute()/sweepToUser() -- for\n * whichever forwarder the caller names.\n *\n * Split out of useRecovery (upstream e5bff75) so several forwarders can be\n * acted on from one render. useRecovery still exists and is unchanged for\n * callers that only care about one target; it is now this hook plus a fetch.\n */\nexport function useRecoveryActions(opts: UseRecoveryActionsOptions = {}): UseRecoveryActionsResult {\n  const optsRef = useRef(opts);\n  optsRef.current = opts;\n\n  const wallet = opts.wallet;\n  const connectedChainId = wallet?.walletChainId;\n\n  const recipient = opts.recipient;\n  const recipientKey = recipient && isAddress(recipient) ? recipient : undefined;\n\n  // Keyed by LOWERCASED forwarder address: the API's checksum casing is not\n  // guaranteed stable across responses, and a casing difference must not orphan\n  // an in-flight attempt's progress from the section that started it.\n  const [states, setStates] = useState<Record<string, RecoveryState>>({});\n\n  const setState = useCallback((forwarder: string, next: RecoveryState) => {\n    setStates((prev) => ({ ...prev, [forwarder.toLowerCase()]: next }));\n  }, []);\n\n  const stateFor = useCallback(\n    (forwarder: string | undefined) => (forwarder ? (states[forwarder.toLowerCase()] ?? IDLE_STATE) : IDLE_STATE),\n    [states],\n  );\n\n  const busy = useMemo(\n    () =>\n      Object.values(states).some(\n        (s) => s.step === \"switching\" || s.step === \"deploying\" || s.step === \"recovering\",\n      ),\n    [states],\n  );\n\n  const recover = useCallback(\n    async (info: RecoveryInfo, action: RecoveryAction, tokenAddress?: string) => {\n      if (!recipientKey || !info?.forwarder) return;\n      const key = info.forwarder;\n\n      setState(key, { step: \"switching\" });\n      try {\n        if (connectedChainId !== BSC_CHAIN_ID) {\n          if (!wallet?.switchChain) {\n            throw new Error(\"wallet cannot switch chains\");\n          }\n          await wallet.switchChain(BSC_CHAIN_ID);\n        }\n\n        if (!wallet?.writeContract || !wallet?.readContract || !wallet?.waitForReceipt) {\n          throw new Error(\"wallet is missing contract capabilities required for recovery\");\n        }\n\n        // Independent address verification BEFORE any signature (security\n        // report L-003/L-008). Both writes below aim at addresses that arrived\n        // over HTTP -- info.factory and info.forwarder -- so this proves,\n        // against a pinned factory list and the chain itself (via the user's\n        // own RPC, not our backend), that the forwarder really is the one this\n        // factory deploys for THIS recipient.\n        //\n        // Placed after the chain switch so the read happens on BSC, and before\n        // the deploy write so a bad response costs the user nothing at all.\n        // Fails closed: an unverifiable address aborts instead of signing.\n        const verdict = await verifyForwarder(wallet.readContract, BSC_CHAIN_ID, {\n          factory: info.factory,\n          forwarder: info.forwarder,\n          // recipientKey, not a field from the response: the point is to prove\n          // the forwarder belongs to the account we are acting for.\n          recipient: recipientKey,\n          version: info.version,\n          // optsRef, not opts: this callback's dep list deliberately omits\n          // option fields (same reason onError is read through the ref), so a\n          // direct `opts.` read here would capture a stale list.\n          trusted: optsRef.current.trustedFactories,\n        });\n        if (!verdict.ok) {\n          throw new Error(`address verification failed: ${verdict.reason}`);\n        }\n\n        let deployTxHash: string | undefined;\n\n        // Permissionless deploy: any address can call factory.deploy(recipient, version) for\n        // any recipient -- it always resolves to the same CREATE2 address, so this is safe to\n        // (re)issue even if another actor deployed it in the meantime (the tx would just be a\n        // no-op revert, not a fund-routing risk either way, since deploy takes no funds).\n        if (!info.isDeployed) {\n          setState(key, { step: \"deploying\" });\n          deployTxHash = await wallet.writeContract({\n            address: info.factory as Hex,\n            abi: standingForwarderFactoryAbi,\n            functionName: \"deploy\",\n            args: [recipientKey as Hex, BigInt(info.version)],\n            chainId: BSC_CHAIN_ID,\n          });\n          setState(key, { step: \"deploying\", deployTxHash });\n          // The receipt's STATUS decides, not its existence (upstream 07354b7).\n          // waitForReceipt resolves for a reverted tx exactly as it does for a\n          // successful one, so discarding the return value reported every\n          // revert as a completed recovery -- the user saw \"done\" while the\n          // forwarder was never deployed and their funds had not moved. A\n          // reverted deploy also stops HERE, before the action write, which\n          // would otherwise spend a second fee on a call that cannot succeed.\n          const deployReceipt = await wallet.waitForReceipt({ chainId: BSC_CHAIN_ID, hash: deployTxHash as Hex });\n          if (deployReceipt.status === \"reverted\") {\n            throw new Error(`forwarder deploy reverted on-chain (tx ${deployTxHash})`);\n          }\n          // The forwarder now exists on-chain. Tell the owner so info.isDeployed\n          // re-reads true: if the action write below fails and the user retries,\n          // we must NOT re-issue deploy() (it would revert against the\n          // already-deployed CREATE2 address). This attempt continues on the\n          // stable `info` argument; the refetch only affects retries.\n          optsRef.current.onChanged?.();\n        }\n\n        setState(key, { step: \"recovering\", deployTxHash });\n        const actionTxHash =\n          action === \"execute\"\n            ? await wallet.writeContract({\n                address: info.forwarder as Hex,\n                abi: standingForwarderAbi,\n                functionName: \"execute\",\n                args: [],\n                chainId: BSC_CHAIN_ID,\n              })\n            : await wallet.writeContract({\n                address: info.forwarder as Hex,\n                abi: standingForwarderAbi,\n                functionName: \"sweepToUser\",\n                args: [(tokenAddress ?? primaryRecoverableToken(info)) as Hex],\n                chainId: BSC_CHAIN_ID,\n              });\n\n        setState(key, { step: \"recovering\", deployTxHash, actionTxHash });\n        const actionReceipt = await wallet.waitForReceipt({ chainId: BSC_CHAIN_ID, hash: actionTxHash as Hex });\n        if (actionReceipt.status === \"reverted\") {\n          // Thrown, so it lands in the catch below and surfaces as step 'error'\n          // with the tx hash in the message. Reporting 'done' here would tell a\n          // user their stuck funds had been recovered when nothing moved.\n          throw new Error(`recovery transaction reverted on-chain (tx ${actionTxHash})`);\n        }\n\n        setState(key, { step: \"done\", deployTxHash, actionTxHash });\n        optsRef.current.onRecoverySuccess?.({\n          action,\n          forwarder: info.forwarder,\n          actionTxHash,\n          ...(deployTxHash ? { deployTxHash } : {}),\n        });\n        optsRef.current.onChanged?.();\n      } catch (err) {\n        const e = err instanceof Error ? err : new Error(String(err));\n        setStates((prev) => {\n          const k = key.toLowerCase();\n          return { ...prev, [k]: { ...(prev[k] ?? IDLE_STATE), step: \"error\", error: e.message } };\n        });\n        optsRef.current.onError?.(e);\n      }\n    },\n    [recipientKey, connectedChainId, wallet, setState],\n  );\n\n  return { recover, stateFor, busy };\n}\n\nexport interface UseRecoveryOptions {\n  /** Pre-built client. Takes precedence over apiBaseUrl. */\n  client?: RelayClient;\n  /** Used to build a client via createRelayClient when `client` isn't passed. */\n  apiBaseUrl?: string;\n  /** The EVM address recovery status/actions are for -- normally the connected wallet's own\n   * address. Funds always route here (the forwarder's immutable recipient), never anywhere\n   * else, regardless of which action is taken. */\n  recipient?: string;\n  /**\n   * The currently selected CROSS delivery target symbol -- threaded into\n   * GET /v1/recovery's \"?target=\" so a depositor-kind (e.g. pONEUSD) target reports\n   * ITS OWN forwarder/factory instead of the default swap one. Omitted/undefined selects the\n   * configured default, byte-identical to pre-multi-target behavior.\n   */\n  target?: string;\n  /** Gate on the fetch (default true). A caller that already fetched every target's\n   * recovery info (RecoverFunds in per-forwarder mode) disables this hook's own\n   * request rather than re-requesting what it just received (upstream e5bff75). */\n  enabled?: boolean;\n  /** Optional automatic REST refresh cadence in milliseconds. Disabled when omitted or <= 0. */\n  refetchInterval?: number;\n  /**\n   * Injected wallet capabilities (dapp-ui depends on neither wagmi nor viem, so\n   * the host supplies these). Every one that's missing degrades gracefully --\n   * no `walletChainId` means \"unknown current chain\", and a missing\n   * writeContract/readContract/waitForReceipt fails `recover()` closed rather\n   * than crashing the widget.\n   */\n  wallet?: RelayWalletProps;\n  /** Called when a GET /v1/recovery request 401s. */\n  onUnauthorized?: () => void;\n  onError?: (error: Error) => void;\n  /** Called once after the action receipt confirms successfully. */\n  onRecoverySuccess?: (result: RecoverySuccessResult) => void;\n  /** See UseRecoveryActionsOptions.trustedFactories. */\n  trustedFactories?: readonly string[];\n}\n\nexport interface UseRecoveryResult {\n  info?: RecoveryInfo;\n  loading: boolean;\n  error?: Error;\n  /** Re-fetches GET /v1/recovery for the current recipient (also called automatically once a\n   * recovery attempt completes, so the panel reflects the new on-chain state). */\n  refresh: () => void;\n  /** Signs the requested recovery tx for this hook's own fetched `info` -- see\n   * UseRecoveryActionsResult.recover for semantics. */\n  recover: (action: RecoveryAction, tokenAddress?: string) => Promise<void>;\n  recoveryState: RecoveryState;\n}\n\nexport function useRecovery(opts: UseRecoveryOptions = {}): UseRecoveryResult {\n  const optsRef = useRef(opts);\n  optsRef.current = opts;\n\n  const client = useMemo(() => {\n    if (opts.client) return opts.client;\n    if (!opts.apiBaseUrl) {\n      throw new Error(\"useRecovery: either `client` or `apiBaseUrl` must be provided\");\n    }\n    return createRelayClient({ baseUrl: opts.apiBaseUrl });\n  }, [opts.client, opts.apiBaseUrl]);\n\n  const recipient = opts.recipient;\n  const recipientKey = recipient && isAddress(recipient) ? recipient : undefined;\n\n  // ---- GET /v1/recovery -------------------------------------------------\n  const [info, setInfo] = useState<RecoveryInfo>();\n  const [loading, setLoading] = useState(false);\n  const [error, setError] = useState<Error>();\n  const [refreshNonce, setRefreshNonce] = useState(0);\n\n  const target = opts.target;\n  const enabled = opts.enabled ?? true;\n  const refetchInterval = opts.refetchInterval;\n\n  useEffect(() => {\n    if (!recipientKey || !enabled) {\n      setInfo(undefined);\n      setError(undefined);\n      setLoading(false);\n      return;\n    }\n\n    let cancelled = false;\n    let inFlight = false;\n    let isFirstFetch = true;\n\n    async function refetchRecovery() {\n      if (cancelled || inFlight) return;\n      inFlight = true;\n      if (isFirstFetch) setLoading(true);\n      try {\n        const result = await client.getRecovery({ user: recipientKey as string, target });\n        if (cancelled) return;\n        setInfo(result);\n        setError(undefined);\n      } catch (err) {\n        if (cancelled) return;\n        const e = err instanceof Error ? err : new Error(String(err));\n        setError(e);\n        optsRef.current.onError?.(e);\n        if (e instanceof RelayApiError && e.status === 401) {\n          optsRef.current.onUnauthorized?.();\n        }\n      } finally {\n        inFlight = false;\n        if (!cancelled && isFirstFetch) setLoading(false);\n        isFirstFetch = false;\n      }\n    }\n\n    void refetchRecovery();\n    const refetchTimer =\n      typeof refetchInterval === \"number\" &&\n      Number.isFinite(refetchInterval) &&\n      refetchInterval > 0\n        ? setInterval(() => {\n            void refetchRecovery();\n          }, refetchInterval)\n        : undefined;\n\n    return () => {\n      cancelled = true;\n      if (refetchTimer) clearInterval(refetchTimer);\n    };\n    // refreshNonce is a write-only trigger -- bumping it re-runs this fetch.\n  }, [client, recipientKey, refreshNonce, target, enabled, refetchInterval]);\n\n  const refresh = useCallback(() => setRefreshNonce((n) => n + 1), []);\n\n  // ---- recover(action) -------------------------------------------------\n  // The write path itself now lives in useRecoveryActions (which takes the\n  // forwarder's info as an ARGUMENT so a caller can drive several forwarders\n  // from one render). This hook is the single-target binding onto it: it feeds\n  // its own fetched `info` in, and reports back only that forwarder's state.\n  const actions = useRecoveryActions({\n    recipient: opts.recipient,\n    wallet: opts.wallet,\n    trustedFactories: opts.trustedFactories,\n    onError: opts.onError,\n    onRecoverySuccess: opts.onRecoverySuccess,\n    onChanged: refresh,\n  });\n\n  const recover = useCallback(\n    async (action: RecoveryAction, tokenAddress?: string) => {\n      if (!info) return;\n      await actions.recover(info, action, tokenAddress);\n    },\n    [actions, info],\n  );\n\n  const recoveryState = actions.stateFor(info?.forwarder);\n\n  return { info, loading, error, refresh, recover, recoveryState };\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Typed client for the orchestrator's stateless REST API tier. Every\n// endpoint here corresponds 1:1 with a route registered in\n// orchestrator/internal/api/server.go -- see ./types.ts for the wire\n// shapes, hand mirrored from that Go package's JSON tags. Ported from\n// frontend/src/api.ts, decoupled from import.meta.env: baseUrl is passed\n// explicitly to createRelayClient so this module has no framework or\n// bundler-env coupling (no React, no wagmi, no import.meta.env).\nimport type {\n  ApiErrorBody,\n  Catalog,\n  DepositAddressRequest,\n  DepositAddressResult,\n  Order,\n  OrderSummary,\n  QuoteRequest,\n  QuoteResult,\n  RecoveryInfo,\n  RecoveryRequest,\n} from './types'\n\n/** Thrown for any non-2xx API response. `status` is the HTTP status code. */\nexport class RelayApiError extends Error {\n  status: number\n  /** Parsed JSON error body when the response was valid JSON, else undefined. */\n  body?: unknown\n\n  constructor(status: number, message: string, body?: unknown) {\n    super(message)\n    this.name = 'RelayApiError'\n    this.status = status\n    this.body = body\n  }\n}\n\nexport interface RelayClientOptions {\n  /** API origin, e.g. \"https://orchestrator.example.com\". Trailing slashes are stripped. */\n  baseUrl: string\n}\n\nexport interface RelayClient {\n  /** GET /v1/config -- the curated origin catalog + fixed crossd destination. */\n  getConfig(): Promise<Catalog>\n  /** POST /v1/quote -- a price-only preview, no order/deposit is created. */\n  getQuote(req: QuoteRequest): Promise<QuoteResult>\n  /**\n   * GET /v1/deposit-address -- resolves the stable, reusable deposit\n   * address for (user, originChainId, originCurrency). No amount is\n   * involved and no order is created; the same address can receive any\n   * number of deposits of any size. Several deposits can be aggregated into\n   * one order when they jointly fund the same reusable forwarder.\n   *\n   * `target` (Task 8) is the delivery asset's symbol from\n   * `Catalog.targets` -- sent as `&target=<symbol>` only when it's a\n   * non-empty string; omitted otherwise, in which case the backend\n   * resolves the default target (byte-identical to pre-Task-6 behavior).\n   */\n  getDepositAddress(req: DepositAddressRequest, target?: string): Promise<DepositAddressResult>\n  /**\n   * GET /v1/orders/{orderId} -- current status + stepper progress for one\n   * order. Reads are public (unauthenticated).\n   */\n  getOrder(orderId: string): Promise<Order>\n  /**\n   * GET /v1/orders?user=... -- every order swept from deposits to that\n   * user's standing address(es) (newest-first, capped server-side).\n   */\n  listOrders(user: string): Promise<OrderSummary[]>\n  /** Absolute URL of the SSE order-change stream for `user`\n   * (GET /v1/orders/stream?user=...). Emits `event: connected` on open,\n   * `event: orders-changed` when that user's orders change, and `:\n   * heartbeat` comments -- it carries no order data itself, so consumers\n   * always follow up with `listOrders`. */\n  ordersStreamUrl(user: string): string\n  /**\n   * GET /v1/recovery?user=... -- permissionless self-recovery status for a\n   * user's forwarder on BSC (Task 1). `req.target` (optional) selects which\n   * per-target forwarder/factory to report -- omitted selects the\n   * configured default, byte-identical to pre-multi-target behavior.\n   * Purely informational -- fetching it never triggers or blocks any\n   * on-chain action. The response's `tokens[]` (Task 6/7 -- every known BSC\n   * token held at the forwarder) flows through as-is: it's plain JSON, so no\n   * extra parsing/mapping is needed beyond RecoveryInfo's own typing, and\n   * it's simply absent/undefined for an older backend that predates it.\n   */\n  getRecovery(req: RecoveryRequest): Promise<RecoveryInfo>\n}\n\n/**\n * Builds a framework-agnostic Relay API client. Uses plain `fetch` only --\n * no react-query, no RainbowKit/wagmi. `baseUrl` is passed in explicitly (no\n * `import.meta.env` coupling) so the widget can be embedded in any host app's\n * own config/env story.\n *\n * No API key: the backend has no inbound key gate (upstream 83845fb removed\n * the dead `apiKey`/X-API-Key plumbing -- a security control that does not\n * exist must not appear in a public type).\n */\nexport function createRelayClient(options: RelayClientOptions): RelayClient {\n  const baseUrl = options.baseUrl.replace(/\\/+$/, '')\n\n  // The body is read as text (rather than res.json()) so non-2xx responses\n  // with a JSON {\"error\": ...} body and non-JSON error bodies can both be\n  // turned into a useful RelayApiError.\n  async function request<T>(path: string, init?: RequestInit): Promise<T> {\n    const headers: Record<string, string> = {\n      ...(init?.headers as Record<string, string> | undefined),\n    }\n    // Only set Content-Type when there's a request body -- bodiless GETs\n    // (getConfig, getOrder) send no Content-Type, mirroring the original\n    // frontend/src/api.ts request shapes.\n    if (init?.body != null) {\n      headers['Content-Type'] = 'application/json'\n    }\n\n    let res: Response\n    try {\n      res = await fetch(`${baseUrl}${path}`, { ...init, headers })\n    } catch (err) {\n      throw new RelayApiError(0, `network error calling ${path}: ${(err as Error).message}`)\n    }\n\n    const text = await res.text()\n\n    if (!res.ok) {\n      let message = `request to ${path} failed with status ${res.status}`\n      let parsedBody: unknown\n      try {\n        parsedBody = JSON.parse(text) as ApiErrorBody\n        const errBody = parsedBody as ApiErrorBody\n        if (errBody?.error) message = errBody.error\n      } catch {\n        // Non-JSON or empty error body -- keep the generic message above,\n        // and fall back to the raw text so callers still see something.\n        parsedBody = text || undefined\n      }\n      throw new RelayApiError(res.status, message, parsedBody)\n    }\n\n    // A 2xx body is expected to be JSON -- but \"expected\" is not \"guaranteed\"\n    // once anything can sit between the app and the API. A captive portal, a CDN\n    // interstitial or a misrouted proxy answers 200 with HTML, and a bare\n    // JSON.parse then threw a raw SyntaxError straight out of the SDK, bypassing\n    // the RelayApiError contract every other failure path honors and surfacing\n    // in consumer apps as an unhandled exception (advisory ADV-002).\n    //\n    // Folded into the same error type as every other failure, with the raw body\n    // attached so the caller can see what actually arrived.\n    try {\n      return JSON.parse(text) as T\n    } catch {\n      throw new RelayApiError(\n        res.status,\n        `expected a JSON response from ${path} but got ${text ? 'a non-JSON body' : 'an empty body'}`,\n        text || undefined,\n      )\n    }\n  }\n\n  return {\n    getConfig(): Promise<Catalog> {\n      // `targets` (Task 8) defaults to [] when the backend response omits\n      // it entirely -- back-compat with an older backend that predates\n      // per-target delivery (Task 4).\n      return request<Catalog>('/v1/config').then((catalog) => ({\n        ...catalog,\n        targets: catalog.targets ?? [],\n      }))\n    },\n\n    getQuote(req: QuoteRequest): Promise<QuoteResult> {\n      return request<QuoteResult>('/v1/quote', {\n        method: 'POST',\n        body: JSON.stringify(req),\n      })\n    },\n\n    getDepositAddress(req: DepositAddressRequest, target?: string): Promise<DepositAddressResult> {\n      const params = new URLSearchParams({\n        user: req.user,\n        originChainId: String(req.originChainId),\n        originCurrency: req.originCurrency,\n      })\n      if (target) {\n        params.set('target', target)\n      }\n      return request<DepositAddressResult>(`/v1/deposit-address?${params.toString()}`)\n    },\n\n    getOrder(orderId: string): Promise<Order> {\n      return request<Order>(`/v1/orders/${encodeURIComponent(orderId)}`)\n    },\n\n    listOrders(user: string): Promise<OrderSummary[]> {\n      const params = new URLSearchParams({ user })\n      // `?? []` for the same reason getConfig defaults `targets`: a body that\n      // omits (or nulls) the key must not hand `undefined` to OrdersList, which\n      // maps over the result.\n      return request<{ orders?: OrderSummary[] | null }>(`/v1/orders?${params.toString()}`).then(\n        (res) => res.orders ?? [],\n      )\n    },\n\n    ordersStreamUrl(user: string): string {\n      return `${baseUrl}/v1/orders/stream?user=${encodeURIComponent(user)}`\n    },\n\n    getRecovery(req: RecoveryRequest): Promise<RecoveryInfo> {\n      const params = new URLSearchParams({ user: req.user })\n      if (req.target) {\n        params.set('target', req.target)\n      }\n      return request<RecoveryInfo>(`/v1/recovery?${params.toString()}`)\n    },\n  }\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Minimal viem ABIs for the permissionless self-recovery flow (Task 2).\n// Verbatim-verified against the compiled artifacts in\n// contracts/out/StandingForwarderFactory.sol/StandingForwarderFactory.json\n// and contracts/out/StandingForwarder.sol/StandingForwarder.json -- do NOT\n// hand-edit signatures here without re-checking those artifacts, a mismatch\n// would silently encode the wrong calldata.\n//\n// Kept intentionally tiny (only the 3 functions the recovery flow calls) --\n// this is not a general-purpose contract ABI, just enough for viem/wagmi's\n// `writeContract` to type-check `functionName`/`args` correctly.\n\n/** StandingForwarderFactory.deploy(address recipient, uint256 version) returns (address) --\n * mints (deterministically, per recipient+version) the standing forwarder contract for a\n * user who hasn't deployed theirs yet. Permissionless: any address can call this for any\n * recipient, and the deployed forwarder always resolves to the same CREATE2 address. */\nexport const standingForwarderFactoryAbi = [\n  {\n    type: 'function',\n    name: 'deploy',\n    stateMutability: 'nonpayable',\n    inputs: [\n      { name: 'recipient', type: 'address' },\n      { name: 'version', type: 'uint256' },\n    ],\n    outputs: [{ name: '', type: 'address' }],\n  },\n] as const\n\n/** StandingForwarder.execute() and .sweepToUser(address tokenAddr) -- both permissionless:\n * neither takes a destination argument, so funds always route to the forwarder's immutable\n * recipient. `execute()` completes the normal bridge delivery for whatever's currently held;\n * `sweepToUser(tokenAddr)` recovers a stuck ERC-20 balance directly to the recipient instead. */\nexport const standingForwarderAbi = [\n  {\n    type: 'function',\n    name: 'execute',\n    stateMutability: 'nonpayable',\n    inputs: [],\n    outputs: [],\n  },\n  {\n    type: 'function',\n    name: 'sweepToUser',\n    stateMutability: 'nonpayable',\n    inputs: [{ name: 'tokenAddr', type: 'address' }],\n    outputs: [],\n  },\n] as const\n\n/** StandingForwarderFactory / DepositorForwarderFactory\n * `computeAddress(address recipient, uint256 version) view returns (address)`.\n *\n * Both factories expose this with an identical signature (verified against the\n * live BSC contracts), which is what lets one ABI verify either kind. This is\n * the factory's OWN derivation -- the same `Create2.computeAddress(_salt(...),\n * keccak256(_initCode(...)))` its `deploy()` uses -- so it cannot disagree with\n * where the factory actually deploys. See verifyForwarder.ts for why the check\n * is anchored here rather than on a locally embedded creation code. */\nexport const forwarderFactoryComputeAbi = [\n  {\n    type: 'function',\n    name: 'computeAddress',\n    stateMutability: 'view',\n    inputs: [\n      { name: 'recipient', type: 'address' },\n      { name: 'version', type: 'uint256' },\n    ],\n    outputs: [{ name: '', type: 'address' }],\n  },\n] as const\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Independent verification of the addresses the API hands us, before the\n// wallet signs anything against them (security report L-003 / L-008, and the\n// verifiable half of L-005).\n//\n// The problem: GET /v1/recovery and GET /v1/deposit-address return `factory`\n// and `forwarder`, and the widget used to sign straight against those values.\n// A tampered or compromised response could therefore aim `factory.deploy()`\n// and `forwarder.execute()/sweepToUser()` at a contract of the attacker's\n// choosing and the user would be none the wiser -- the UI would look normal.\n//\n// The fix has to be careful about WHERE trust comes from, because the obvious\n// version of it is circular:\n//\n//   * Recomputing the CREATE2 address from values that ALSO came from the API\n//     proves nothing -- a hostile response would simply supply matching\n//     immutables and the derivation would agree with itself.\n//   * Calling `factory.computeAddress()` on an API-supplied factory proves\n//     nothing either -- an attacker's \"factory\" can return whatever it likes,\n//     consistently.\n//\n// So exactly one thing is pinned here, in code, out of the API's reach: the\n// set of factories this deployment ever issues addresses from. Given a trusted\n// factory, the CHAIN then proves the forwarder, read through the user's OWN\n// RPC (wagmi's publicClient) rather than our backend. That is the whole\n// design: pin the smallest possible root, derive the rest from the chain.\n//\n// Pinning the factory list rather than the creation code is deliberate. The\n// alternative -- embedding StandingForwarder's ~5.7KB creation code plus all\n// nine constructor immutables and doing CREATE2 locally -- verifies the same\n// fact while adding ten values that must stay in lockstep with the backend, so\n// any routing-param change silently breaks the widget's verification. A\n// factory address is one value, and `computeAddress` is the factory's own\n// derivation, so it cannot drift from what the factory actually deploys.\nimport type { ReadContractFn } from \"../types\";\nimport { forwarderFactoryComputeAbi } from \"./abis\";\nimport { isAddress } from \"./units\";\n\n/** Every StandingForwarderFactory / DepositorForwarderFactory this deployment\n * has issued deposit addresses from, on BSC.\n *\n * Superseded factories stay listed on purpose. A forwarder's factory is pinned\n * per address at issuance time (`standing_addresses.factory`, copied onto each\n * order), so a user recovering an address minted before a rotation legitimately\n * gets an older factory back from the API -- dropping it here would block that\n * user from recovering their own funds, which is a worse outcome than the\n * narrow trust gain. Every entry is one of ours either way.\n *\n * Rotation (see backends/deploy/RUNBOOK-canary.md): ADD the new factory, keep\n * the old ones. */\nexport const DEFAULT_TRUSTED_FACTORIES: readonly string[] = [\n  // Current, deployed 2026-07-28 with the L-023 native-sweep + L-026 events\n  // bytecode.\n  \"0x486BEEc35A9a9915213Ea02AD3709738335F7702\", // StandingForwarderFactory\n  \"0x3438735303911aBe113AdF14b18eE730cC24893b\", // DepositorForwarderFactory\n  // Superseded by the rotation above -- still trusted for addresses issued\n  // before it (see the note on this constant).\n  \"0xA3B548Ab23043e6B24A087515fe1306379683533\", // StandingForwarderFactory\n  \"0x44317d07378390543d99Ee76374E86C1Ef6cF832\", // DepositorForwarderFactory\n];\n\nexport type VerifyResult = { ok: true } | { ok: false; reason: string };\n\nfunction sameAddress(a: string | undefined, b: string | undefined): boolean {\n  if (!a || !b) return false;\n  return a.toLowerCase() === b.toLowerCase();\n}\n\n/** Whether `factory` is one this deployment issues addresses from. Compared\n * case-insensitively: the API's checksum casing is not guaranteed to match the\n * literals above, and a casing difference is not a security signal. */\nexport function isTrustedFactory(factory: string | undefined, trusted: readonly string[]): boolean {\n  return trusted.some((t) => sameAddress(t, factory));\n}\n\n/**\n * Resolves the effective trusted-factory list from the ops-supplied CDN value\n * (apps.json `relay.forwarderFactories`, flattened by remote-config).\n *\n * The CDN list REPLACES the pinned defaults whenever it carries at least one\n * valid address (2026-07-31 ops decision): S3 is the source of truth for the\n * factory set, so a rotation can retire an old factory -- not just add a new\n * one -- without a redeploy. The pinned DEFAULT_TRUSTED_FACTORIES are only the\n * OFFLINE fallback: an absent/empty CDN value, a fetch failure, or a list\n * where nothing parses as an address keeps the widget working on the shipped\n * set rather than bricking every verification.\n *\n * Security consequence, stated plainly: whoever can write that S3 object\n * controls which factories the pre-signing verification accepts. The bucket's\n * write access must be held to the same bar as a code deploy. A host's\n * explicit `trustedFactories` prop still bypasses this entirely (unchanged).\n */\nexport function mergeTrustedFactories(remote: readonly unknown[] | undefined): readonly string[] {\n  if (!remote || remote.length === 0) return DEFAULT_TRUSTED_FACTORIES;\n  const seen = new Set<string>();\n  const resolved: string[] = [];\n  for (const entry of remote) {\n    // CDN JSON is untyped at runtime -- a malformed entry is skipped, never\n    // thrown on (the widget must not die on an ops typo).\n    if (typeof entry !== \"string\" || !isAddress(entry)) continue;\n    const key = entry.toLowerCase();\n    if (seen.has(key)) continue;\n    seen.add(key);\n    resolved.push(entry);\n  }\n  // Every entry malformed = treat as \"no usable CDN value\", not as an empty\n  // allowlist -- an empty list would fail every recovery closed on a typo.\n  return resolved.length > 0 ? resolved : DEFAULT_TRUSTED_FACTORIES;\n}\n\nexport interface VerifyForwarderArgs {\n  /** The factory the API reported. Checked against the pinned allowlist. */\n  factory: string | undefined;\n  /** The forwarder the API reported. Checked against the factory's own\n   * on-chain derivation. */\n  forwarder: string | undefined;\n  /** The address funds must end up at -- the connected wallet, NOT a value\n   * from the API response. This is what makes the check meaningful: it proves\n   * the forwarder is derived for THIS user. */\n  recipient: string | undefined;\n  version: number | undefined;\n  trusted?: readonly string[];\n}\n\n/**\n * Proves `forwarder` is the address `factory` really deploys for\n * (`recipient`, `version`), by asking the factory on-chain.\n *\n * Fails CLOSED: any inability to complete the check (no client, missing field,\n * an RPC error) returns ok:false. The caller uses this to gate signing, and an\n * unverifiable address is exactly the case where signing should not proceed --\n * treating \"couldn't check\" as \"fine\" would hand back the whole guarantee to\n * anyone able to make the RPC call fail.\n */\nexport async function verifyForwarder(\n  readContract: ReadContractFn | undefined,\n  chainId: number,\n  { factory, forwarder, recipient, version, trusted = DEFAULT_TRUSTED_FACTORIES }: VerifyForwarderArgs,\n): Promise<VerifyResult> {\n  if (!factory || !forwarder || !recipient || version === undefined) {\n    return { ok: false, reason: \"address verification is missing inputs (factory/forwarder/recipient/version)\" };\n  }\n  if (!isTrustedFactory(factory, trusted)) {\n    return {\n      ok: false,\n      reason: `factory ${factory} is not a recognized deposit-address factory -- refusing to sign against it`,\n    };\n  }\n  if (!readContract) {\n    return { ok: false, reason: \"no chain connection available to verify the forwarder address\" };\n  }\n\n  let derived: string;\n  try {\n    derived = (await readContract({\n      chainId,\n      address: factory as `0x${string}`,\n      abi: forwarderFactoryComputeAbi,\n      functionName: \"computeAddress\",\n      args: [recipient as `0x${string}`, BigInt(version)],\n    })) as string;\n  } catch (e) {\n    return {\n      ok: false,\n      reason: `could not verify the forwarder address on-chain: ${e instanceof Error ? e.message : String(e)}`,\n    };\n  }\n\n  if (!sameAddress(derived, forwarder)) {\n    return {\n      ok: false,\n      reason: `forwarder address mismatch: the API returned ${forwarder}, but factory ${factory} derives ${derived} for this account -- refusing to sign`,\n    };\n  }\n  return { ok: true };\n}\n\n/**\n * Proves `forwarder` is derived for `recipient` by SOME pinned factory, without\n * being told which one.\n *\n * Used on the deposit path, where the widget never signs against a factory --\n * it only sends funds -- so the question is simply \"is this one of our\n * forwarders for this user?\". Not needing the API to name the factory makes\n * this strictly stronger than verifyForwarder: there is one less field of the\n * response being taken at face value, and it also means GET /v1/deposit-address\n * does not have to grow a `factory` field.\n *\n * Trying every pinned factory is also what makes this work across target kinds\n * without special-casing: a swap-kind target's forwarder derives from the\n * standing factory and a depositor-kind target's from its own, and either one\n * matching is a pass. Reads run concurrently -- there are a handful of\n * factories, and a per-factory round-trip in sequence would be needless latency\n * in front of a user action.\n *\n * Fails CLOSED, same as verifyForwarder: if no factory derives it -- including\n * because every read failed -- the answer is no.\n */\nexport async function verifyForwarderFromAnyFactory(\n  readContract: ReadContractFn | undefined,\n  chainId: number,\n  {\n    forwarder,\n    recipient,\n    version,\n    trusted = DEFAULT_TRUSTED_FACTORIES,\n  }: Omit<VerifyForwarderArgs, \"factory\">,\n): Promise<VerifyResult> {\n  if (!forwarder || !recipient || version === undefined) {\n    return { ok: false, reason: \"address verification is missing inputs (forwarder/recipient/version)\" };\n  }\n  if (!readContract) {\n    return {\n      ok: false,\n      reason:\n        \"no BSC chain connection available to verify the deposit forwarder -- add BNB Smart Chain (chain 56) to your wagmi config\",\n    };\n  }\n\n  const derived = await Promise.all(\n    trusted.map(async (factory) => {\n      try {\n        return (await readContract({\n          chainId,\n          address: factory as `0x${string}`,\n          abi: forwarderFactoryComputeAbi,\n          functionName: \"computeAddress\",\n          args: [recipient as `0x${string}`, BigInt(version)],\n        })) as string;\n      } catch {\n        // One unreachable//non-factory entry must not veto the others.\n        return undefined;\n      }\n    }),\n  );\n\n  if (derived.some((d) => sameAddress(d, forwarder))) return { ok: true };\n  if (derived.every((d) => d === undefined)) {\n    return { ok: false, reason: \"could not reach any deposit-address factory on BSC to verify the forwarder\" };\n  }\n  return {\n    ok: false,\n    reason: `forwarder address mismatch: ${forwarder} is not the address any known factory derives for this account -- refusing to deposit`,\n  };\n}\n\n/**\n * Whether the deposit address the user is about to send funds to is one we can\n * derive ourselves.\n *\n * This is the honest boundary of L-005. On every bridged route the deposit\n * address is Relay's own open deposit address: it is not a CREATE2 address of\n * ours, nothing on-chain derives it, and no client-side computation can confirm\n * it -- the residual is documented rather than papered over. What the client\n * CAN do on those routes is verify the `forwarder` in the same response\n * (verifyForwarder above), so a response tampered in that field is caught.\n *\n * The direct BSC route is the exception and is fully verifiable: there is\n * nothing for Relay to move, so the deposit address IS the forwarder, and the\n * on-chain check therefore covers the exact address receiving the funds.\n */\nexport function depositAddressIsForwarder(depositAddress: string | undefined, forwarder: string | undefined): boolean {\n  return sameAddress(depositAddress, forwarder);\n}\n","// Ported from relay-protocol packages/relay-widget/src/recoveryGroups.ts\n// (upstream e5bff75). Pure grouping for the \"Recover stuck funds\" panel --\n// no React, no wallet, no fetch.\n//\n// The unit the panel must be organized around is the FORWARDER ADDRESS, not the\n// delivery-token symbol, and getting that wrong is not cosmetic.\n//\n// A forwarder is derived from (recipient, version) plus the factory and\n// constructor args of the target that issued it (see the backend's\n// forwarderForTargetAt). For a \"depositor\"-kind target (e.g. pONEUSD) the\n// target's own factory and depositor go into the derivation, so its forwarder is\n// genuinely distinct. For a \"swap\"-kind target (e.g. ONEUSD) the target does NOT\n// enter the derivation at all -- every swap-kind target resolves to the SAME\n// forwarder for the same recipient.\n//\n// So a panel laid out per target would, the moment a deployment offers two\n// swap-kind targets, show one balance twice with a Recover button under each.\n// Both buttons would move the whole balance and the second would revert on\n// `NothingToForward` -- which is precisely the hazard that makes per-ORIGIN\n// splitting unacceptable (one balance, no amount parameter on sweepToUser), now\n// reintroduced on a different axis. Grouping by forwarder is what keeps \"one\n// balance, one button\" true no matter how the catalog's targets are configured.\n//\n// It also fixes the aggregate count: summing a per-target count double-counted\n// every forwarder shared between targets. See countRecoverableTokens.\nimport type { OrderSummary, RecoveryInfo } from './types'\n\n/** The backend state in which a forwarder still HOLDS the funds an order is\n * about. Funds leave at BSC_FUNDED -> BRIDGE_INITIATED, so this is the only\n * status whose deposits can explain a live balance -- see the backend's\n * domain.allowed state map. */\nconst FUNDS_AT_FORWARDER_STATUS = 'BSC_FUNDED'\n\n/** One delivery target and the recovery info fetched for it. */\nexport interface RecoveryTargetEntry {\n  /** Delivery-token symbol from `Catalog.targets`, e.g. \"pONEUSD\". */\n  symbol: string\n  /** True for the catalog's default target. Orders report `target: \"\"` for it,\n   * so matching an order to a target needs to know which symbol \"\" means. */\n  isDefault: boolean\n  info: RecoveryInfo\n}\n\n/** One deposit that makes up a forwarder's current balance. A read-only\n * provenance row -- see ForwarderGroup.deposits for why it can never become a\n * per-origin recover button. */\nexport interface GroupedDeposit {\n  /** Amount that arrived at the forwarder, in BSC USDT base units. */\n  amount: string\n  /** The chain the user actually paid from. Absent for an unattributed deposit\n   * (a direct BSC deposit has no cross-chain leg and never gets one). */\n  originChainId?: number\n  /** The user's own send on the origin chain. Absent when unattributed. */\n  originTxHash?: string\n  /** The BSC-side arrival. Always present -- it is how the deposit was seen. */\n  txHash: string\n  blockNumber: number\n}\n\n/** One forwarder, the delivery targets that share it, and everything the panel\n * needs to render exactly one recovery section for it. */\nexport interface ForwarderGroup {\n  /** The forwarder address, in the casing the API reported it. */\n  forwarder: string\n  /** Every delivery-token symbol resolving to this forwarder, in catalog order.\n   * More than one means they share it by derivation (all swap-kind targets do). */\n  targets: string[]\n  /** The recovery info for this forwarder. When several targets share it, they\n   * report identical forwarder/balance/tokens, and their factory is identical by\n   * config invariant (a swap-kind target's factory must be empty or the standing\n   * factory), so the first entry's info stands for the group. A mismatched\n   * factory would in any case fail closed at signing: verifyForwarder checks it\n   * against the pinned list AND against the chain's own derivation. */\n  info: RecoveryInfo\n  /**\n   * The individual deposits behind this forwarder's current balance, oldest\n   * first -- read-only provenance, so a user who topped up from two chains can\n   * see that the single balance is made of their two sends.\n   *\n   * It is deliberately NOT a set of per-origin actions. There is one balance in\n   * one contract and `sweepToUser` takes no amount, so splitting the button per\n   * origin would have both buttons move the entire balance with the second\n   * reverting. Showing the composition answers the question (\"where did my 2.0\n   * come from?\") without inventing an action that cannot exist.\n   *\n   * Empty when no order for this forwarder is currently BSC_FUNDED -- e.g. a\n   * deposit seen on-chain but not yet swept into an order, or residual dust from\n   * an order already written off. That is exactly why the sum of these rows can\n   * be less than the balance, and why callers must say so rather than present\n   * this as a complete decomposition (see depositsTotal).\n   */\n  deposits: GroupedDeposit[]\n  /** Sum of `deposits[].amount`, in the same base units as `info.balance`.\n   * Compare the two: when this is smaller, part of the balance has no listed\n   * provenance and the UI must not imply otherwise. */\n  depositsTotal: bigint\n}\n\n/** Parses a base-unit amount string, treating anything malformed as zero.\n * Amounts here are display/provenance only -- never load-bearing -- so one bad\n * row must not throw during render (the same rule every other formatter in this\n * package follows). */\nfunction toAmount(raw: string | undefined): bigint {\n  if (!raw) return 0n\n  try {\n    const v = BigInt(raw)\n    return v > 0n ? v : 0n\n  } catch {\n    return 0n\n  }\n}\n\n/** The target symbol an order refers to: its own, or the catalog default's when\n * the order reports \"\" (the backend's encoding for \"the default target\"). */\nfunction orderTarget(order: OrderSummary, defaultSymbol: string | undefined): string | undefined {\n  return order.target || defaultSymbol\n}\n\n/**\n * Groups per-target recovery info by forwarder address, attaching the deposits\n * that explain each forwarder's balance.\n *\n * Entries are expected in catalog order; the returned groups follow the order in\n * which their forwarder was first seen, so the layout is stable across refetches\n * rather than reordering under the user.\n *\n * Addresses are keyed case-insensitively: the API's checksum casing is not\n * guaranteed to be stable, and two spellings of one address must never become\n * two sections with two buttons on the same balance.\n */\nexport function groupForwarders(\n  entries: readonly RecoveryTargetEntry[],\n  orders: readonly OrderSummary[] = [],\n): ForwarderGroup[] {\n  const defaultSymbol = entries.find((e) => e.isDefault)?.symbol\n\n  const byForwarder = new Map<string, ForwarderGroup>()\n  for (const entry of entries) {\n    if (!entry.info?.forwarder) continue\n    const key = entry.info.forwarder.toLowerCase()\n    const existing = byForwarder.get(key)\n    if (existing) {\n      existing.targets.push(entry.symbol)\n      continue\n    }\n    byForwarder.set(key, {\n      forwarder: entry.info.forwarder,\n      targets: [entry.symbol],\n      info: entry.info,\n      deposits: [],\n      depositsTotal: 0n,\n    })\n  }\n\n  // Second pass, once every group knows its full target set: an order maps to a\n  // forwarder through its target, so this cannot be folded into the loop above\n  // without missing orders belonging to a target seen later.\n  for (const group of byForwarder.values()) {\n    const symbols = new Set(group.targets)\n    for (const order of orders) {\n      if (order.status !== FUNDS_AT_FORWARDER_STATUS) continue\n      const target = orderTarget(order, defaultSymbol)\n      if (!target || !symbols.has(target)) continue\n      for (const d of order.deposits ?? []) {\n        group.deposits.push({\n          amount: d.amount,\n          originChainId: d.originChainId,\n          originTxHash: d.originTxHash,\n          txHash: d.txHash,\n          blockNumber: d.blockNumber,\n        })\n        group.depositsTotal += toAmount(d.amount)\n      }\n    }\n    group.deposits.sort((a, b) => a.blockNumber - b.blockNumber)\n  }\n\n  return [...byForwarder.values()]\n}\n\n/** Groups holding something worth acting on -- a recoverable token, or (for a\n * backend predating `tokens[]`) a nonzero primary balance the server hasn't\n * called \"none\". These are the sections the panel renders. */\nexport function groupsWithFunds(groups: readonly ForwarderGroup[]): ForwarderGroup[] {\n  return groups.filter((g) => {\n    if ((g.info.tokens ?? []).some((t) => t.recoverable)) return true\n    return toAmount(g.info.balance) > 0n && g.info.suggestedAction !== 'none'\n  })\n}\n\n/**\n * Total recoverable tokens across every forwarder -- the gear badge's number.\n *\n * Counting per forwarder rather than per target is the point: two targets\n * sharing a forwarder describe ONE pot of money, and summing their per-target\n * counts reported it twice. The badge then read higher than the panel could ever\n * list, which looks like the panel is hiding something.\n */\nexport function countRecoverableTokens(groups: readonly ForwarderGroup[]): number {\n  return groups.reduce((sum, g) => sum + (g.info.tokens ?? []).filter((t) => t.recoverable).length, 0)\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as Dialog from \"@radix-ui/react-dialog\";\nimport { Drawer } from \"vaul\";\nimport { useHistoryDismiss } from \"../../../../hooks/use-history-dismiss\";\nimport { cn, isInAppBrowser } from \"../../../../lib/utils\";\nimport {\n  RelayShellContext,\n  useRelayShellContext,\n} from \"../../providers/relay-shell-provider\";\nimport type { RelayDrawerDirection, RelayTheme } from \"../../types\";\nimport \"../../styles/shell.css\";\n\nexport interface ResponsiveShellProps {\n  theme?: RelayTheme;\n  mobileBreakpoint?: number;\n  drawerDirection?: RelayDrawerDirection;\n  dialogWidth?: string;\n  drawerMaxWidth?: string;\n  drawerMinWidth?: string;\n  style?: React.CSSProperties;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  children: React.ReactNode;\n}\n\ninterface KeyboardViewportInset {\n  keyboardHeight: number;\n  /**\n   * 키보드 중 \"고정 위치 콘텐츠를 아직 덮고 있는\" 높이.\n   *\n   * `position: fixed` 의 기준은 레이아웃 뷰포트(ICB)다. iOS WKWebView(지갑\n   * 인앱 브라우저 포함)는 키보드가 떠도 레이아웃 뷰포트를 안 줄이므로 드로어\n   * 하단이 키보드 밑으로 들어간다 — 그만큼 올려줘야 한다. 반대로 안드로이드\n   * webview 처럼 뷰포트 자체가 줄어드는 환경은 브라우저가 이미 처리한 것이라\n   * 또 올리면 키보드 위에 빈 틈이 생긴다. 줄어든 양을 빼서 두 경우를 한 식으로\n   * 덮는다(부분적으로만 줄어드는 환경도 자연히 맞는다).\n   */\n  keyboardOverlap: number;\n  viewportHeight: number;\n}\n\nconst INPUT_SCROLL_MARGIN = 24;\n\nfunction isTextInput(target: EventTarget | null): target is HTMLElement {\n  return (\n    target instanceof HTMLElement &&\n    target.matches(\"input, textarea, select\")\n  );\n}\n\nfunction getInputScrollDelta(\n  targetRect: DOMRect,\n  scrollAreaRect: DOMRect,\n  viewport?: VisualViewport | null,\n): number {\n  const viewportTop = viewport?.offsetTop ?? scrollAreaRect.top;\n  const viewportBottom = viewport\n    ? viewport.offsetTop + viewport.height\n    : scrollAreaRect.bottom;\n  const safeTop =\n    Math.max(scrollAreaRect.top, viewportTop) + INPUT_SCROLL_MARGIN;\n  const safeBottom =\n    Math.min(scrollAreaRect.bottom, viewportBottom) - INPUT_SCROLL_MARGIN;\n\n  if (targetRect.top < safeTop) return targetRect.top - safeTop;\n  if (targetRect.bottom > safeBottom) return targetRect.bottom - safeBottom;\n  return 0;\n}\n\n/** Tracks the part of the layout viewport occupied by a mobile soft keyboard. */\nfunction useKeyboardViewportInset(): KeyboardViewportInset {\n  const layoutViewportHeightRef = React.useRef(0);\n  const [inset, setInset] = React.useState<KeyboardViewportInset>({\n    keyboardHeight: 0,\n    keyboardOverlap: 0,\n    viewportHeight: 0,\n  });\n\n  React.useEffect(() => {\n    const viewport = window.visualViewport;\n    if (!viewport) return;\n\n    const update = () => {\n      const layoutHeight = Math.max(\n        window.innerHeight,\n        document.documentElement.clientHeight,\n      );\n      layoutViewportHeightRef.current = Math.max(\n        layoutViewportHeightRef.current,\n        layoutHeight,\n        viewport.height + viewport.offsetTop,\n      );\n      const keyboardHeight = Math.max(\n        0,\n        layoutViewportHeightRef.current -\n          viewport.height -\n          viewport.offsetTop,\n      );\n      const layoutShrink = Math.max(\n        0,\n        layoutViewportHeightRef.current - layoutHeight,\n      );\n      const keyboardOverlap = Math.max(0, keyboardHeight - layoutShrink);\n      const viewportHeight = viewport.height;\n      setInset((current) =>\n        current.keyboardHeight === keyboardHeight &&\n        current.keyboardOverlap === keyboardOverlap &&\n        current.viewportHeight === viewportHeight\n          ? current\n          : { keyboardHeight, keyboardOverlap, viewportHeight },\n      );\n    };\n\n    update();\n    viewport.addEventListener(\"resize\", update);\n    viewport.addEventListener(\"scroll\", update);\n    return () => {\n      viewport.removeEventListener(\"resize\", update);\n      viewport.removeEventListener(\"scroll\", update);\n    };\n  }, []);\n\n  return inset;\n}\n\n/**\n * `useMediaQuery` with a synchronous initial value.\n *\n * The shared hook (`hooks/use-media-query.ts`) starts at `false` and corrects\n * itself in an effect, which is the right call for SSR-hydrated markup but\n * wrong for a shell that can be MOUNTED already-open on a phone: it would paint\n * the dialog branch for a frame and then swap to the drawer. Reading\n * `matchMedia` in the state initializer avoids that frame; the subscription\n * below then tracks the viewport LIVE, so crossing the breakpoint while the\n * modal is open swaps Dialog <-> Drawer immediately (the wizard's state lives\n * above this component -- see RelayWizardStateHost.tsx -- and survives the swap).\n */\nfunction useIsMobileViewport(mobileBreakpoint: number): boolean {\n  const query = `(max-width: ${mobileBreakpoint}px)`;\n  const [isMobile, setIsMobile] = React.useState(\n    () =>\n      typeof window !== \"undefined\" &&\n      typeof window.matchMedia === \"function\" &&\n      window.matchMedia(query).matches,\n  );\n\n  React.useEffect(() => {\n    if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n    const mql = window.matchMedia(query);\n    setIsMobile(mql.matches);\n    const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);\n    mql.addEventListener(\"change\", handler);\n    return () => mql.removeEventListener(\"change\", handler);\n  }, [query]);\n\n  return isMobile;\n}\n\n/**\n * Shared responsive modal shell for the relay deposit widget — radix\n * `Dialog` on desktop, vaul `Drawer` on mobile, switching at\n * `mobileBreakpoint`. Generalized from\n * `WalletConnectModal`/`WalletConnectModalTrigger`/`WalletConnectModalContent`\n * (`packages/dapp-ui/src/features/wallet-connect-modal/components/`): same\n * controlled/uncontrolled open handling, same size-var CSS merge, same\n * Drawer/Dialog branch — but exposing plain compound exports\n * (`ResponsiveShell` / `ShellTrigger` / `ShellContent`) instead of\n * `Object.assign` static properties, since the relay widget owns its own\n * body content rather than a fixed wallet list.\n *\n * The branch follows the viewport LIVE, including while the modal is open:\n * swapping `Dialog.Root` <-> `Drawer.Root` still unmounts everything below it\n * (portal content included), so any consumer with per-open state to preserve\n * must hold that state ABOVE this component — which is exactly what\n * `<RelayDeposit>` does via `RelayWizardStateHost`.\n */\nexport function ResponsiveShell({\n  theme = \"dark\",\n  mobileBreakpoint = 768,\n  drawerDirection = \"bottom\",\n  dialogWidth,\n  drawerMaxWidth,\n  drawerMinWidth,\n  style,\n  open: openProp,\n  onOpenChange,\n  children,\n}: ResponsiveShellProps) {\n  const isMobile = useIsMobileViewport(mobileBreakpoint);\n  const [internalOpen, setInternalOpen] = React.useState(false);\n\n  const isControlled = openProp !== undefined;\n  const open = isControlled ? openProp : internalOpen;\n\n  const handleOpenChange = React.useCallback(\n    (value: boolean) => {\n      if (!isControlled) setInternalOpen(value);\n      onOpenChange?.(value);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  // Android 백버튼 / iOS 백스와이프로 닫히게 한다.\n  useHistoryDismiss(open, React.useCallback(() => handleOpenChange(false), [handleOpenChange]));\n\n  const mergedStyle = React.useMemo(() => {\n    const sizeVars: Record<string, string> = {};\n    if (dialogWidth) sizeVars[\"--rd-dialog-width\"] = dialogWidth;\n    if (drawerMaxWidth) sizeVars[\"--rd-drawer-max-width\"] = drawerMaxWidth;\n    if (drawerMinWidth) sizeVars[\"--rd-drawer-min-width\"] = drawerMinWidth;\n    return { ...sizeVars, ...style };\n  }, [dialogWidth, drawerMaxWidth, drawerMinWidth, style]);\n\n  const contextValue = React.useMemo(\n    () => ({\n      open,\n      setOpen: handleOpenChange,\n      isMobile,\n      theme,\n      drawerDirection,\n      portalStyle: mergedStyle,\n    }),\n    [open, handleOpenChange, isMobile, theme, drawerDirection, mergedStyle],\n  );\n\n  const wrapper = (\n    <div data-theme={theme} style={mergedStyle}>\n      {children}\n    </div>\n  );\n\n  if (isMobile) {\n    return (\n      <RelayShellContext.Provider value={contextValue}>\n        <Drawer.Root\n          key=\"drawer\"\n          direction={drawerDirection}\n          open={open}\n          onOpenChange={handleOpenChange}\n          noBodyStyles\n        >\n          {wrapper}\n        </Drawer.Root>\n      </RelayShellContext.Provider>\n    );\n  }\n\n  return (\n    <RelayShellContext.Provider value={contextValue}>\n      <Dialog.Root open={open} onOpenChange={handleOpenChange}>\n        {wrapper}\n      </Dialog.Root>\n    </RelayShellContext.Provider>\n  );\n}\n\nexport function ShellTrigger({\n  asChild,\n  children,\n  dataAttributes,\n}: {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  /**\n   * Extra `data-*` attributes to land on the ACTUAL trigger element -- i.e. on\n   * the host's own child under `asChild`, since radix/vaul merge a Trigger's\n   * props onto the child it renders as.\n   *\n   * Exists so a compound can expose state a host needs for its own styling\n   * without that host having to re-derive it: `<RelayRecovery.Trigger>` passes\n   * `data-rd-recovery-count`, letting a custom trigger render its own badge from\n   * the same aggregate the default button uses. Deliberately narrow (data\n   * attributes only, no arbitrary prop spread) so the shell stays a shell.\n   */\n  dataAttributes?: Record<`data-${string}`, string | number | undefined>;\n}) {\n  const { isMobile } = useRelayShellContext();\n  const T = isMobile ? Drawer.Trigger : Dialog.Trigger;\n  return (\n    <T asChild={asChild} {...dataAttributes}>\n      {children}\n    </T>\n  );\n}\n\nexport function ShellContent({\n  className,\n  ariaTitle,\n  children,\n}: {\n  className?: string;\n  ariaTitle: string;\n  children: React.ReactNode;\n}) {\n  const { open, isMobile, theme, drawerDirection, portalStyle } =\n    useRelayShellContext();\n  const { keyboardHeight, keyboardOverlap, viewportHeight } =\n    useKeyboardViewportInset();\n  const isInApp = isInAppBrowser();\n  const drawerRef = React.useRef<HTMLDivElement | null>(null);\n  const focusTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  React.useEffect(\n    () => () => {\n      if (focusTimerRef.current) clearTimeout(focusTimerRef.current);\n    },\n    [],\n  );\n\n  // 열림/닫힘 경계에서만 스크롤을 처음으로 되돌린다. 키보드가 내려갈 때는\n  // 건드리지 않는다 — 예전에는 되돌렸는데, 그러면 입력 직후 입력창 아래에\n  // 붙는 요약/정보 블록이 화면 밖으로 밀려나 \"입력창만 보이는\" 상태가 됐다.\n  React.useEffect(() => {\n    if (focusTimerRef.current) clearTimeout(focusTimerRef.current);\n    const active = document.activeElement;\n    if (!open && isTextInput(active) && drawerRef.current?.contains(active)) {\n      active.blur();\n    }\n    drawerRef.current\n      ?.querySelectorAll<HTMLElement>(\"[data-rd-scroll-area]\")\n      .forEach((scrollArea) => {\n        scrollArea.scrollTo?.({ top: 0, behavior: \"auto\" });\n      });\n  }, [open]);\n\n  // 인앱 브라우저(UA)로 걸러내지 않는다 — 지갑 인앱이라도 iOS 는 레이아웃\n  // 뷰포트가 안 줄어들어 보정이 필요하고, 줄어드는 환경은 `keyboardOverlap` 이\n  // 알아서 0 에 수렴한다. `dvh` 는 키보드를 모르므로 max-height 는 항상 실제\n  // 비주얼 뷰포트로 다시 조인다.\n  const shouldAdjustForKeyboard =\n    isMobile && drawerDirection === \"bottom\" && keyboardHeight > 0;\n  const keyboardStyle: React.CSSProperties | undefined =\n    shouldAdjustForKeyboard\n      ? {\n          bottom: keyboardOverlap,\n          maxHeight: `min(85dvh, calc(${viewportHeight}px - env(safe-area-inset-top, 0px)))`,\n        }\n      : undefined;\n\n  const handleFocusCapture = React.useCallback(\n    (event: React.FocusEvent<HTMLDivElement>) => {\n      if (!isMobile || drawerDirection !== \"bottom\" || !isTextInput(event.target)) {\n        return;\n      }\n      const target = event.target;\n      const scrollArea = target.closest<HTMLElement>(\"[data-rd-scroll-area]\");\n      if (!scrollArea) return;\n\n      if (focusTimerRef.current) clearTimeout(focusTimerRef.current);\n      focusTimerRef.current = setTimeout(() => {\n        const delta = getInputScrollDelta(\n          target.getBoundingClientRect(),\n          scrollArea.getBoundingClientRect(),\n          window.visualViewport,\n        );\n        if (Math.abs(delta) >= 1) {\n          scrollArea.scrollBy?.({ top: delta, behavior: \"auto\" });\n        }\n        focusTimerRef.current = null;\n      }, 280);\n    },\n    [drawerDirection, isMobile],\n  );\n\n  if (isMobile) {\n    return (\n      <Drawer.Portal>\n        <Drawer.Overlay className=\"rd-overlay\" />\n        <Drawer.Content\n          ref={drawerRef}\n          className={cn(\"rd-drawer-content\", className)}\n          data-theme={theme}\n          data-rd-shell=\"drawer\"\n          data-rd-in-app={isInApp ? \"true\" : \"false\"}\n          data-rd-keyboard-adjusted={\n            shouldAdjustForKeyboard ? \"true\" : \"false\"\n          }\n          style={{ ...portalStyle, ...keyboardStyle }}\n          onFocusCapture={handleFocusCapture}\n        >\n          <Drawer.Title className=\"rd-sr-only\">{ariaTitle}</Drawer.Title>\n          <div className=\"rd-drawer-handle\" />\n          {children}\n        </Drawer.Content>\n      </Drawer.Portal>\n    );\n  }\n\n  return (\n    <Dialog.Portal>\n      <Dialog.Overlay className=\"rd-overlay\" />\n      <Dialog.Content\n        className={cn(\"rd-dialog-content\", className)}\n        data-theme={theme}\n        data-rd-shell=\"dialog\"\n        style={portalStyle}\n      >\n        <Dialog.Title className=\"rd-sr-only\">{ariaTitle}</Dialog.Title>\n        <Dialog.Description className=\"rd-sr-only\">\n          {ariaTitle}\n        </Dialog.Description>\n        {children}\n      </Dialog.Content>\n    </Dialog.Portal>\n  );\n}\n","import styleInject from '#style-inject';styleInject(\"html body[data-scroll-locked]{overflow-y:auto!important;margin-right:0!important}.rd-overlay{position:fixed;inset:0;z-index:100000;background:var(--ds-overlay-dim, rgba(0, 0, 0, .5));-webkit-backdrop-filter:var(--ds-bg-blur-7, blur(3.5px));backdrop-filter:var(--ds-bg-blur-7, blur(3.5px))}.rd-dialog-content{-webkit-font-smoothing:antialiased;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:100001;width:var(--rd-dialog-width, 480px);max-height:90vh;overflow:hidden;display:flex;flex-direction:column;background:var(--ds-surface-default, #ffffff);border:1px solid var(--ds-border-default, #00000012);border-radius:24px;box-shadow:var(--ds-shadow-modal, 0 4px 30px -1px rgba(0, 0, 0, .15));outline:none;font-family:inherit}.rd-dialog-content.rd-deposit-shell{height:min(40rem,90vh)}.rd-drawer-content{-webkit-font-smoothing:antialiased;position:fixed;bottom:0;left:0;right:0;z-index:100001;width:100%;max-width:var(--rd-drawer-max-width, none);min-width:var(--rd-drawer-min-width, 0);max-height:min(85dvh,calc(100svh - env(safe-area-inset-top,0px)));margin:0 auto;display:flex;flex-direction:column;background:var(--ds-surface-default, #ffffff);border:1px solid var(--ds-border-default, #00000012);border-top-left-radius:16px;border-top-right-radius:16px;outline:none;font-family:inherit;box-sizing:border-box;overscroll-behavior:contain}.rd-drawer-content.rd-deposit-shell[data-vaul-drawer-direction=bottom],.rd-drawer-content.rd-deposit-shell[data-vaul-drawer-direction=top]{height:min(40rem,85dvh,calc(100svh - env(safe-area-inset-top,0px)))}.rd-drawer-content[data-vaul-drawer-direction=right]{inset:0 0 0 auto;max-height:100vh;width:var(--rd-drawer-max-width, 400px);max-width:100vw;border-radius:16px 0 0 16px}.rd-drawer-content[data-vaul-drawer-direction=left]{inset:0 auto 0 0;max-height:100vh;width:var(--rd-drawer-max-width, 400px);max-width:100vw;border-radius:0 16px 16px 0}.rd-drawer-content[data-vaul-drawer-direction=top]{inset:0 0 auto;border-radius:0 0 16px 16px}.rd-drawer-handle{width:60px;height:4px;border-radius:999px;background:var(--ds-border-medium, rgba(0, 0, 0, .18));margin:12px auto 8px;flex-shrink:0}.rd-drawer-content[data-vaul-drawer-direction=left] .rd-drawer-handle,.rd-drawer-content[data-vaul-drawer-direction=right] .rd-drawer-handle{display:none}.rd-overlay{animation:rd-overlay-show .18s ease-out}.rd-overlay[data-state=closed]{animation:rd-overlay-hide .15s ease-in}.rd-dialog-content{animation:rd-dialog-show .2s ease-out}.rd-dialog-content[data-state=closed]{animation:rd-dialog-hide .15s ease-in}@keyframes rd-overlay-show{0%{opacity:0}}@keyframes rd-overlay-hide{to{opacity:0}}@keyframes rd-dialog-show{0%{opacity:0;transform:translate(-50%,-48%) scale(.96)}}@keyframes rd-dialog-hide{to{opacity:0;transform:translate(-50%,-48%) scale(.96)}}@media(prefers-reduced-motion:reduce){.rd-overlay,.rd-overlay[data-state=closed],.rd-dialog-content,.rd-dialog-content[data-state=closed]{animation:none}}.rd-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}\\n\")","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport { ShellTrigger } from \"./shell/ResponsiveShell\";\n\nexport interface RelayDepositTriggerProps {\n  /** Render the child as the trigger instead of wrapping it. Defaults to\n   * `true` whenever a child is supplied, matching\n   * `WalletConnectModalTrigger`'s `asChild ?? children != null`. */\n  asChild?: boolean;\n  children?: ReactNode;\n}\n\n/**\n * Thin pass-through to `ShellTrigger` (radix `Dialog.Trigger` on desktop /\n * vaul `Drawer.Trigger` on mobile) so hosts write\n * `<RelayDeposit.Trigger><button>…</button></RelayDeposit.Trigger>` without\n * knowing which primitive is active. Falls back to a plain default button when\n * no child is given, same as `WalletConnectModalTrigger`.\n *\n * Wrapped in `<TrackingBoundary feature=\"relay\">` for the same reason\n * `WalletConnectModalTrigger` is: the `data-track=\"open\"` attribute below (and\n * on any host-supplied child that carries one) is only picked up by the\n * analytics delegate a boundary installs. Without it the open click is silently\n * untracked, since the trigger lives OUTSIDE the portaled `<Content>` boundary.\n */\nexport function RelayDepositTrigger({ asChild, children }: RelayDepositTriggerProps) {\n  return (\n    <TrackingBoundary feature=\"relay\">\n      <ShellTrigger asChild={asChild ?? children != null}>\n        {children ?? (\n          <button type=\"button\" data-track=\"open\">\n            Deposit\n          </button>\n        )}\n      </ShellTrigger>\n    </TrackingBoundary>\n  );\n}\n","\"use client\";\n\n// The deposit wizard's state, lifted out of the portal.\n//\n// `RelayWizardStateHost` is rendered by `<RelayDeposit>` ABOVE\n// `ResponsiveShell` and stays mounted for the widget's whole life; the engine\n// that actually owns the state (`RelayWizardEngine`, everything\n// `RelayDepositBody` used to compute) is a SIBLING of the shell, mounted ONLY\n// while the modal is open. That split is what makes a live dialog<->drawer\n// switch non-destructive: crossing the 768px breakpoint swaps the shell's\n// subtree (radix `Dialog.Root` <-> vaul `Drawer.Root`, portal content\n// included), but the engine is outside it and never unmounts, so the typed\n// amount, selected origin/target, the fetched deposit address and any in-flight\n// tx tracking all survive.\n//\n// Mount-only-while-open also keeps the two properties the old in-portal\n// placement gave for free:\n//   * no catalog/deposit-address/quote request before the modal is opened, and\n//   * a full reset on close -- the engine unmounts, so there is no state left\n//     to reset (the old `WalletInfo.tsx`-style reset-on-close effect is gone).\n//\n// See providers/relay-wizard-provider.tsx for why the bundle travels through a\n// store rather than a plain context value.\nimport * as React from \"react\";\n\nimport { trackDappUiFunnel } from \"../../../lib/sentry/index\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport {\n  useRemoteConfigValue,\n  type AppsConfig,\n  resolveEnvValue,\n  resolveRelayForwarderFactories,\n} from \"../../../lib/remote-config\";\nimport { useDepositAddress, type UseDepositAddressOptions } from \"../hooks/useDepositAddress\";\nimport { useRelayOrders } from \"../hooks/useRelayOrders\";\nimport { useTargetTokens } from \"../hooks/useTargetTokens\";\nimport { resolveRelayApiBaseUrl } from \"../lib/api-base\";\nimport { createRelayClient } from \"../lib/client\";\nimport { countRecoverableTokens, groupForwarders, type RecoveryTargetEntry } from \"../lib/recoveryGroups\";\nimport { mergeTrustedFactories } from \"../lib/verifyForwarder\";\nimport type { OrderSummary, RecoveryInfo } from \"../lib/types\";\nimport { formatUnits, isAddress, parseUnits } from \"../lib/units\";\nimport { useRelayDepositContext } from \"../providers/relay-deposit-provider\";\nimport {\n  RelayWizardStoreContext,\n  createRelayWizardStore,\n  type RelayWizardBundle,\n  type RelayDepositView,\n  type RelayWizardStore,\n} from \"../providers/relay-wizard-provider\";\nimport type { RelayDepositProps, RelayWalletProps } from \"../types\";\n\nconst DEPOSIT_STATUS_POLL_MS = 1_000;\n\nfunction propsToOpts(\n  props: RelayDepositProps,\n  target: string | undefined,\n  wallet: RelayWalletProps,\n  resolvedApiBaseUrl: string,\n  trustedFactories: readonly string[] | undefined,\n): UseDepositAddressOptions {\n  const requireWalletConnection =\n    props.requireWalletConnection ?? (props.showRecipientInput === false);\n  const connectedWallet =\n    wallet.walletAddress && isAddress(wallet.walletAddress)\n      ? wallet.walletAddress\n      : undefined;\n\n  return {\n    client: props.client,\n    apiBaseUrl: resolvedApiBaseUrl,\n    wallet,\n    chains: props.chains,\n    // In wallet-locked mode, an empty CONTROLLED recipient clears any address\n    // retained by the hook after disconnect. When connected, pinning the\n    // recipient to that wallet prevents a stale/host-controlled address from\n    // redirecting deposits while the UI promises wallet credit.\n    recipient: requireWalletConnection\n      ? connectedWallet ?? \"\"\n      : props.recipient,\n    // A controlled amount can otherwise keep POST /quote alive after logout.\n    amount: requireWalletConnection && !connectedWallet ? \"\" : props.amount,\n    defaultToken: props.defaultToken,\n    target,\n    trustedFactories,\n    onDepositAddress: props.onDepositAddress,\n    onDeposited: props.onDeposited,\n    onError: props.onError,\n  };\n}\n\n/** Parses a human decimal amount into smallest units, or 0n if empty/invalid/zero. */\nfunction toSmallestUnits(human: string, decimals: number): bigint {\n  const trimmed = human.trim();\n  if (!trimmed) return 0n;\n  try {\n    const value = parseUnits(trimmed, decimals);\n    return value > 0n ? value : 0n;\n  } catch {\n    return 0n;\n  }\n}\n\nfunction orderMatchesForwarder(\n  order: OrderSummary,\n  forwarderVersion: number | undefined,\n  targetSymbol: string | undefined,\n  targetIsDefault: boolean,\n): boolean {\n  if (forwarderVersion !== undefined && order.forwarderVersion !== forwarderVersion) return false;\n  const orderTarget = order.target?.trim();\n  if (!orderTarget) return targetIsDefault;\n  return Boolean(targetSymbol && orderTarget.toLowerCase() === targetSymbol.toLowerCase());\n}\n\nfunction orderDepositKey(orderId: string, txHash: string, blockNumber: number): string {\n  return `${orderId}:${txHash.toLowerCase()}:${blockNumber}`;\n}\n\nfunction isFailureStatus(status: string): boolean {\n  return status === \"FAILED\" || status === \"REFUNDED\" || status === \"RELAY_REFUNDED\" || status === \"EXPIRED\";\n}\n\nexport interface RelayWizardStateHostProps {\n  /** The lifted modal open state, owned by `<RelayDeposit>` itself. */\n  open: boolean;\n  children: React.ReactNode;\n}\n\nexport function RelayWizardStateHost({ open, children }: RelayWizardStateHostProps) {\n  // One store per widget instance, created lazily and never replaced -- the\n  // provider value is therefore stable, which is exactly why the shell below\n  // never remounts when `open` flips.\n  const [store] = React.useState(createRelayWizardStore);\n\n  return (\n    <RelayWizardStoreContext.Provider value={store}>\n      {/* Sibling, not wrapper: `children` keeps its position (and its mounted\n          instances) whether or not the engine is present. */}\n      {open && <RelayWizardEngine store={store} />}\n      {children}\n    </RelayWizardStoreContext.Provider>\n  );\n}\n\n/**\n * Every hook the wizard needs, run outside the modal shell. Renders nothing --\n * its only output is the bundle it publishes into `store` after each commit.\n *\n * Body ported verbatim from `RelayDepositBody` (pre-lift), minus the\n * reset-on-close effect (unmounting is the reset now) and plus the `opened`\n * funnel event, which moved here from `<RelayDeposit.Content>` so it fires once\n * per open instead of again on every dialog<->drawer swap.\n */\nfunction RelayWizardEngine({ store }: { store: RelayWizardStore }) {\n  const props = useRelayDepositContext();\n  const { showTargetSelector = true } = props;\n  const requireWalletConnection =\n    props.requireWalletConnection ?? (props.showRecipientInput === false);\n  const hasValidConnectedWallet = Boolean(\n    props.walletAddress && isAddress(props.walletAddress),\n  );\n\n  // Lives above ResponsiveShell so shell swaps and tab changes cannot reset the\n  // deposit form or an in-flight transaction.\n  const [activeView, setActiveView] = React.useState<RelayDepositView>(\"deposit\");\n  const recoveryOpen = activeView === \"recover\";\n  const setRecoveryOpen = React.useCallback<React.Dispatch<React.SetStateAction<boolean>>>(\n    (next) => {\n      setActiveView((current) => {\n        const open = current === \"recover\";\n        const shouldOpen = typeof next === \"function\" ? next(open) : next;\n        return shouldOpen ? \"recover\" : \"deposit\";\n      });\n    },\n    [],\n  );\n\n  React.useEffect(() => {\n    trackDappUiFunnel(\"relay\", \"opened\");\n  }, []);\n\n  // Selected delivery target, threaded into useDepositAddress's `target` option\n  // below. useTargetTokens needs `catalog.targets` -- which is itself part of\n  // useDepositAddress's OWN return value -- so the symbol can't be computed\n  // before this render's useDepositAddress call at all; instead it's synced into\n  // its own state via the effect below, one render behind useTargetTokens'\n  // `selected`. That extra render is what makes BOTH the initial catalog-driven\n  // default AND a later user-driven `setSelectedTarget` call reach\n  // useDepositAddress's `target` option (a plain ref mutated in the render body\n  // would carry the INITIAL default through fine, since `origin` itself lags\n  // catalog by a render too, but would silently drop a user-driven change --\n  // selecting a new target is a single state update with no further render to\n  // pick up a ref written after this render's useDepositAddress call already\n  // fired).\n  const [targetSymbol, setTargetSymbol] = React.useState<string | undefined>(undefined);\n\n  // Resolution order: `client` (built downstream, never needs a URL at all) >\n  // `apiBaseUrl` > `environment`'s built-in default map. Computed once here\n  // and threaded into every place that used to read `props.apiBaseUrl`\n  // directly (propsToOpts, the recovery-badge client below, and the\n  // <RecoverFunds> panel) so they all agree on the same resolved base URL.\n  const resolvedApiBaseUrl = React.useMemo(\n    () => resolveRelayApiBaseUrl({ apiBaseUrl: props.apiBaseUrl, environment: props.environment }),\n    [props.apiBaseUrl, props.environment],\n  );\n\n  // The injected capabilities, re-bundled into the shape useDepositAddress\n  // takes. Memoized on the individual functions so a host passing stable\n  // references (as the hook's own doc requires) doesn't get a fresh `wallet`\n  // object -- and therefore a re-fired balance/receipt effect -- every render.\n  const wallet = React.useMemo<RelayWalletProps>(\n    () => ({\n      walletAddress: props.walletAddress,\n      walletChainId: props.walletChainId,\n      sendTransaction: props.sendTransaction,\n      writeContract: props.writeContract,\n      readContract: props.readContract,\n      switchChain: props.switchChain,\n      waitForReceipt: props.waitForReceipt,\n      getBalance: props.getBalance,\n    }),\n    [\n      props.walletAddress,\n      props.walletChainId,\n      props.sendTransaction,\n      props.writeContract,\n      props.readContract,\n      props.switchChain,\n      props.waitForReceipt,\n      props.getBalance,\n    ],\n  );\n\n  // CDN apps.json relay ops-knobs, read ONCE up here because the trusted\n  // factory list must exist before the useDepositAddress call below (its\n  // one-click verification consumes it); min/max are read further down from\n  // the same object. Only role-labelled `forwarderFactories` is read by current\n  // code; a legacy `trustedFactories` array is deliberately ignored here. The\n  // resolved list REPLACES the pinned\n  // defaults. A host's explicit prop still wins.\n  const resolvedEnv = resolveEnvironment(props.environment);\n  const remoteRelayConfig = useRemoteConfigValue<\n    AppsConfig,\n    { min?: number | string; max?: number | string; trustedFactories?: string[] }\n  >(\"apps.json\", resolvedEnv, (config) => ({\n    min: resolveEnvValue(config?.relay?.minDepositAmount, resolvedEnv),\n    max: resolveEnvValue(config?.relay?.maxDepositAmount, resolvedEnv),\n    trustedFactories: resolveRelayForwarderFactories(config, resolvedEnv),\n  }));\n  const effectiveTrustedFactories = React.useMemo(\n    () => props.trustedFactories ?? mergeTrustedFactories(remoteRelayConfig?.trustedFactories),\n    [props.trustedFactories, remoteRelayConfig?.trustedFactories],\n  );\n\n  const {\n    isConnected,\n    catalog,\n    catalogLoading,\n    catalogError,\n    origin,\n    setOrigin,\n    amount,\n    setAmount,\n    recipient,\n    setRecipient,\n    recipientValid,\n    balance,\n    balanceLoading,\n    insufficientBalance,\n    depositAddress,\n    depositAddressResult,\n    depositAddressLoading,\n    depositAddressError,\n    quote,\n    quoteLoading,\n    quoteError,\n    canDeposit,\n    deposit,\n    depositState,\n  } = useDepositAddress(propsToOpts(props, targetSymbol, wallet, resolvedApiBaseUrl, effectiveTrustedFactories));\n\n  const {\n    options: targetOptions,\n    selected: selectedTarget,\n    setSelected: setSelectedTarget,\n  } = useTargetTokens({\n    catalogTargets: catalog?.targets ?? [],\n    filter: props.targetTokens,\n    defaultTarget: props.defaultTarget,\n  });\n\n  React.useEffect(() => {\n    setTargetSymbol(selectedTarget?.symbol);\n  }, [selectedTarget?.symbol]);\n\n  // Amount/recipient inputs are hidden on SourceForm when the host controls\n  // them via props -- the host owns showing/editing those values elsewhere.\n  // `showRecipientInput: false` additionally hides the recipient field even\n  // when uncontrolled (wallet-locked hosts): the recipient then only ever\n  // comes from the connected wallet's default.\n  const showAmountInput = props.amount === undefined && !props.hideAmount;\n  const showRecipientInput =\n    !requireWalletConnection &&\n    (props.showRecipientInput ?? true) &&\n    props.recipient === undefined;\n\n  // propsToOpts pins this value to the connected wallet in guarded mode, so\n  // the same predicate safely gates address-bound orders/recovery reads too.\n  const hasResolvedRecipient = recipientValid && recipient.trim().length > 0 && isAddress(recipient);\n\n  // Neither a connected wallet nor a usable recipient (host-supplied or typed):\n  // there is no identity to mint a deposit address for yet, so a connect prompt\n  // is shown -- as a NOTICE ABOVE the form, never instead of it. The source\n  // widget deliberately lets a DISCONNECTED user type a recipient into\n  // SourceForm and get a deposit address for it (no wallet required), so hiding\n  // the form here would remove a real capability. The notice disappears as soon\n  // as either identity source resolves.\n  const controlledRecipient =\n    !requireWalletConnection &&\n    props.recipient !== undefined &&\n    props.recipient.trim().length > 0;\n  const showConnectPrompt = requireWalletConnection\n    ? !hasValidConnectedWallet || !hasResolvedRecipient\n    : !isConnected && !controlledRecipient && !hasResolvedRecipient;\n  // The source widget's own hint, kept for the remaining cases the connect\n  // prompt doesn't speak to (e.g. a connected user who cleared/broke the\n  // recipient field) so the two are never on screen together.\n  const showRecipientHint = Boolean(catalog) && !hasResolvedRecipient && !showConnectPrompt;\n\n  // Gates the recovery panel actions and the badge fan-out effect below. The\n  // Recover tab itself is always rendered so the Deposit navigation remains\n  // stable while wallet capabilities resolve or change. The\n  // spec (§6) and useRecovery.recover()'s own guard (useRecovery.ts:220) agree\n  // that recovery needs `writeContract` + `readContract` + `waitForReceipt` --\n  // missing any one of the three means recover() would throw immediately.\n  // `getBalance`/`sendTransaction` are irrelevant here -- recovery never reads\n  // a balance or does a plain transfer.\n  // Opt-in (default OFF) on top of the capability trio: recovery signs on BSC,\n  // which embedded-wallet users can never do (same reasoning as one-click).\n  const canRecover =\n    (props.showRecovery ?? false) &&\n    hasValidConnectedWallet &&\n    Boolean(wallet.writeContract && wallet.readContract && wallet.waitForReceipt);\n\n  // ---- Aggregate recoverable-token badge -----------------------------------\n  // A standalone client, built the same way useDepositAddress/useRecovery each\n  // build their own -- the wizard fetches GET /v1/recovery per catalog target\n  // ITSELF (not via useRecovery, which only ever tracks the ONE\n  // currently-selected target for the open <RecoverFunds> panel) so the gear's\n  // badge can reflect stuck funds across every target, even ones the user\n  // hasn't selected.\n  const recoveryClient = React.useMemo(() => {\n    if (props.client) return props.client;\n    return createRelayClient({ baseUrl: resolvedApiBaseUrl });\n  }, [props.client, resolvedApiBaseUrl]);\n\n  // The full RecoveryInfo per target, not a count (upstream e5bff75). The\n  // <RecoverFunds> panel needs the whole response (forwarder, factory,\n  // isDeployed, tokens[]) to render a section and sign for EACH forwarder, and\n  // this is already exactly the data being fetched -- so the panel consumes it\n  // directly instead of refetching the one selected target for itself.\n  const [recoveryByTarget, setRecoveryByTarget] = React.useState<Record<string, RecoveryInfo>>({});\n  const [recoveryTargetsLoading, setRecoveryTargetsLoading] = React.useState(false);\n  // Bumped by <RecoverFunds> after a recovery changes on-chain state, so the\n  // fetch below re-runs and the panel reflects the new balances/deploy state.\n  const [recoveryNonce, setRecoveryNonce] = React.useState(0);\n  const refreshRecovery = React.useCallback(() => setRecoveryNonce((n) => n + 1), []);\n  const depositConfirmed = depositState.isConfirmed;\n\n  // One orders subscription is shared by the live deposit monitor and the\n  // recovery panel. Keeping a second EventSource for the panel would waste the\n  // API's per-IP/global SSE connection budget. The stream carries no data: the\n  // hook re-fetches /orders after every invalidation and every 1s while this\n  // popup is open. The selected forwarder's /recovery read has its own 1s\n  // polling effect below, because a sub-minimum arrival may not create or\n  // change an order and therefore may emit no SSE signal at all.\n  const ordersWatchEnabled =\n    hasResolvedRecipient && (Boolean(depositAddressResult) || activeView !== \"deposit\");\n  const {\n    orders: watchedOrders,\n    ordersLoading: watchedOrdersLoading,\n    ordersError: watchedOrdersError,\n    ordersInitialized: watchedOrdersInitialized,\n    ordersForRecipient,\n  } = useRelayOrders({\n    client: recoveryClient,\n    recipient: ordersWatchEnabled ? recipient : undefined,\n    enabled: ordersWatchEnabled,\n    // This subscription exists only while the Deposit engine is mounted/open.\n    // SSE remains the immediate signal; the short REST fallback makes success\n    // observable when a deployment has no event bus or an event is dropped.\n    refetchInterval: DEPOSIT_STATUS_POLL_MS,\n  });\n  const recoveryOrders = watchedOrders;\n\n  const currentTarget =\n    selectedTarget ??\n    catalog?.targets.find((target) => target.isDefault);\n  const currentTargetSymbol = currentTarget?.symbol ?? catalog?.destination.symbol;\n  const currentTargetIsDefault = currentTarget?.isDefault ?? true;\n  const currentForwarderVersion = depositAddressResult?.version;\n  const matchingOrders = React.useMemo(\n    () =>\n      watchedOrders.filter((order) =>\n        orderMatchesForwarder(\n          order,\n          currentForwarderVersion,\n          currentTargetSymbol,\n          currentTargetIsDefault,\n        ),\n      ),\n    [watchedOrders, currentForwarderVersion, currentTargetSymbol, currentTargetIsDefault],\n  );\n\n  // Baseline existing orders/deposits once the first snapshot for THIS\n  // recipient is accepted. A reusable forwarder can aggregate a later top-up\n  // into an existing order, so detecting only unseen orderIds misses the exact\n  // business case this monitor exists for; deposit txHash+block is the primary\n  // identity and a new orderId is only the pre-attribution fallback.\n  const depositWatchBaselineRef = React.useRef<\n    | {\n        key: string;\n        orderIds: Set<string>;\n        depositKeys: Set<string>;\n        statuses: Map<string, string>;\n      }\n    | undefined\n  >(undefined);\n  const [trackedOrderId, setTrackedOrderId] = React.useState<string>();\n  const [latestArrivalRaw, setLatestArrivalRaw] = React.useState<bigint>();\n  const depositWatchKey = `${recipient.toLowerCase()}:${currentTargetSymbol?.toLowerCase() ?? \"\"}:${\n    currentForwarderVersion ?? \"\"\n  }`;\n\n  React.useEffect(() => {\n    const snapshotMatchesRecipient =\n      ordersForRecipient?.toLowerCase() === recipient.toLowerCase();\n    if (\n      !ordersWatchEnabled ||\n      !watchedOrdersInitialized ||\n      !snapshotMatchesRecipient ||\n      watchedOrdersError\n    ) {\n      if (!ordersWatchEnabled) {\n        depositWatchBaselineRef.current = undefined;\n        setTrackedOrderId(undefined);\n        setLatestArrivalRaw(undefined);\n      }\n      return;\n    }\n\n    const currentOrderIds = new Set(matchingOrders.map((order) => order.orderId));\n    const currentDepositKeys = new Set(\n      matchingOrders.flatMap((order) =>\n        (order.deposits ?? []).map((deposit) =>\n          orderDepositKey(order.orderId, deposit.txHash, deposit.blockNumber),\n        ),\n      ),\n    );\n    const baseline = depositWatchBaselineRef.current;\n    if (!baseline || baseline.key !== depositWatchKey) {\n      depositWatchBaselineRef.current = {\n        key: depositWatchKey,\n        orderIds: currentOrderIds,\n        depositKeys: currentDepositKeys,\n        statuses: new Map(matchingOrders.map((order) => [order.orderId, order.status])),\n      };\n      setTrackedOrderId(undefined);\n      setLatestArrivalRaw(undefined);\n      return;\n    }\n\n    let candidate: OrderSummary | undefined;\n    let arrived = 0n;\n    let hasParsedArrival = false;\n\n    for (const order of matchingOrders) {\n      for (const deposit of order.deposits ?? []) {\n        const key = orderDepositKey(order.orderId, deposit.txHash, deposit.blockNumber);\n        if (baseline.depositKeys.has(key)) continue;\n        candidate ??= order;\n        try {\n          arrived += BigInt(deposit.amount);\n          hasParsedArrival = true;\n        } catch {\n          // The order still counts as detected; omit only the malformed amount.\n        }\n      }\n    }\n\n    candidate ??= matchingOrders.find((order) => !baseline.orderIds.has(order.orderId));\n    // The backend can publish an order status before deposit attribution lands.\n    // In that case the same orderId/deposits snapshot changes only by status;\n    // comparing statuses makes both SSE refetches and the 1s REST fallback able\n    // to surface funded/processing/completed without waiting for deposits[].\n    candidate ??= matchingOrders.find(\n      (order) => baseline.statuses.get(order.orderId) !== order.status,\n    );\n    // Seen identities are monotonic for this open. A temporarily lagging\n    // replica may omit a row for one fetch; replacing the sets would announce\n    // it as a brand-new deposit when it reappears.\n    for (const orderId of currentOrderIds) baseline.orderIds.add(orderId);\n    for (const depositKey of currentDepositKeys) baseline.depositKeys.add(depositKey);\n    for (const order of matchingOrders) baseline.statuses.set(order.orderId, order.status);\n\n    if (candidate) {\n      setTrackedOrderId(candidate.orderId);\n      if (hasParsedArrival) setLatestArrivalRaw(arrived);\n    }\n  }, [\n    depositWatchKey,\n    matchingOrders,\n    ordersForRecipient,\n    ordersWatchEnabled,\n    recipient,\n    watchedOrdersError,\n    watchedOrdersInitialized,\n  ]);\n\n  React.useEffect(() => {\n    const targets = catalog?.targets ?? [];\n    // Nothing to aggregate without a resolved recipient, enabled recovery\n    // capabilities, or any targets at all. The tab remains visible, but no\n    // badge request is needed while its actions are unavailable.\n    // `recoveryClient` itself is always present now\n    // (client > apiBaseUrl > environment default).\n    if (!canRecover || !hasResolvedRecipient || targets.length === 0) {\n      setRecoveryByTarget({});\n      setRecoveryTargetsLoading(false);\n      return;\n    }\n\n    let cancelled = false;\n    setRecoveryTargetsLoading(true);\n    Promise.all(\n      targets.map((t) => recoveryClient.getRecovery({ user: recipient, target: t.symbol }).catch(() => undefined)),\n    ).then((results) => {\n      if (cancelled) return;\n      const byTarget: Record<string, RecoveryInfo> = {};\n      targets.forEach((t, i) => {\n        const info = results[i];\n        if (info) byTarget[t.symbol] = info;\n      });\n      setRecoveryByTarget(byTarget);\n      setRecoveryTargetsLoading(false);\n    });\n\n    return () => {\n      cancelled = true;\n    };\n    // `depositConfirmed` is the refresh signal (the source widget used its\n    // `orders` reference, which this modal no longer fetches): a deposit landing\n    // on-chain is exactly when a target's recoverable-token set may have\n    // changed. It's never read below -- write-only trigger, same pattern as\n    // useRecovery's own `refreshNonce`. `recoveryNonce` is the panel's own\n    // post-recovery refresh, same idiom.\n  }, [recoveryClient, canRecover, hasResolvedRecipient, recipient, catalog, depositConfirmed, recoveryNonce]);\n\n  // Per-target entries in CATALOG order, which is the order the panel's\n  // sections follow.\n  const recoveryTargets = React.useMemo<RecoveryTargetEntry[]>(\n    () =>\n      (catalog?.targets ?? [])\n        .map((t) => ({ symbol: t.symbol, isDefault: t.isDefault, info: recoveryByTarget[t.symbol] }))\n        .filter((e): e is RecoveryTargetEntry => e.info !== undefined),\n    [catalog, recoveryByTarget],\n  );\n\n  // Counted per FORWARDER, not per target. Several delivery tokens can resolve\n  // to the same forwarder -- every swap-kind target does -- and that is ONE pot\n  // of money, so summing the per-target counts reported it once per target. The\n  // badge then read higher than the panel could ever list, which looks like the\n  // panel is hiding funds. See ../lib/recoveryGroups.ts.\n  const recoverableCount = React.useMemo(\n    () => countRecoverableTokens(groupForwarders(recoveryTargets)),\n    [recoveryTargets],\n  );\n\n  // ---- Already-deposited, still-un-swept balance ----------------------------\n  // A DELIBERATELY separate fetch from the badge aggregation above, for the\n  // SELECTED target only, and gated ONLY on a resolved recipient -- no\n  // capability trio, no `showRecovery` opt-in. Rationale: this one feeds the\n  // minimum-deposit NOTICE, which is pure read-only guidance (\"your next\n  // deposit only has to cover the remainder\"). Every user who can be told a\n  // minimum deserves to be told it accounts for what they already sent, whether\n  // or not their wallet could ever sign the recovery transaction that the gear\n  // offers. Failure (or an older/absent field) degrades to the plain notice.\n  const [pendingSweep, setPendingSweep] = React.useState<\n    { balance: bigint; decimals: number; symbol?: string } | undefined\n  >(undefined);\n  const [selectedRecovery, setSelectedRecovery] = React.useState<RecoveryInfo>();\n\n  // `targetSymbol` lags `selectedTarget` by one render (see its declaration), so\n  // on a multi-target catalog there is a render where the catalog is loaded but\n  // the selected target hasn't reached this state yet. Waiting for it keeps the\n  // fetch to exactly ONE request per (recipient, target, origin) -- no throwaway\n  // request against the backend's default target. A `targets: []` catalog never\n  // resolves a target at all, and `undefined` is the right query there.\n  const targetSyncPending = Boolean(catalog) && (catalog?.targets.length ?? 0) > 0 && !targetSymbol;\n\n  React.useEffect(() => {\n    // No `origin` means no catalog yet, hence no decimals and no minimum to\n    // fold the pending balance into -- nothing this response could be used for.\n    if (\n      !hasResolvedRecipient ||\n      !origin ||\n      !depositAddressResult ||\n      targetSyncPending\n    ) {\n      setPendingSweep(undefined);\n      setSelectedRecovery(undefined);\n      return;\n    }\n\n    // A below-minimum arrival may not create or mutate an order, so the orders\n    // SSE has nothing to invalidate. Poll the selected forwarder independently\n    // while this engine is mounted (the engine exists only while the popup is\n    // open). Three seconds stays far below the API's 5 rps per-IP limit while\n    // giving the waiting balance visibly responsive feedback.\n    let cancelled = false;\n    let inFlight = false;\n    const expectedForwarder = depositAddressResult.forwarder.toLowerCase();\n    setPendingSweep(undefined);\n    setSelectedRecovery(undefined);\n\n    async function refreshSelectedRecovery() {\n      if (cancelled || inFlight) return;\n      inFlight = true;\n      try {\n        const info = await recoveryClient.getRecovery({\n          user: recipient,\n          target: targetSymbol || undefined,\n        });\n        if (cancelled) return;\n        // The request is target-scoped, but still bind the response to the\n        // exact verified forwarder currently on screen before using its\n        // balance as a success criterion.\n        if (\n          info?.forwarder &&\n          info.forwarder.toLowerCase() !== expectedForwarder\n        ) {\n          setPendingSweep(undefined);\n          setSelectedRecovery(undefined);\n          return;\n        }\n        setSelectedRecovery(info);\n        // Only a \"sweep\" recommendation means \"sitting below the bridge minimum,\n        // waiting to be topped up\" -- \"execute\" is enough on its own and belongs\n        // to the recovery flow, \"none\" has nothing there at all.\n        if (!info || info.suggestedAction !== \"sweep\") {\n          setPendingSweep(undefined);\n          return;\n        }\n        let raw: bigint;\n        try {\n          raw = BigInt(info.balance);\n        } catch {\n          setPendingSweep(undefined);\n          return;\n        }\n        const decimals = Number(info.decimals);\n        if (raw <= 0n || !Number.isInteger(decimals) || decimals < 0) {\n          setPendingSweep(undefined);\n          return;\n        }\n        // `RecoveryInfo` carries no top-level symbol -- the only honest source is\n        // a `tokens[]` entry for the same address as the top-level `token`\n        // (always BSC USDT). Absent (older backend), the notice says the amount\n        // without a symbol rather than guessing one.\n        const symbol = info.tokens?.find(\n          (t) => t.address?.toLowerCase() === info.token?.toLowerCase() && t.symbol,\n        )?.symbol;\n        setPendingSweep({ balance: raw, decimals, symbol });\n      } catch {\n        // Keep the last accepted snapshot during a transient polling failure.\n        // Clearing it every three seconds would make the status card flicker.\n      } finally {\n        inFlight = false;\n      }\n    }\n\n    void refreshSelectedRecovery();\n    const recoveryPollTimer = setInterval(() => {\n      void refreshSelectedRecovery();\n    }, DEPOSIT_STATUS_POLL_MS);\n\n    return () => {\n      cancelled = true;\n      clearInterval(recoveryPollTimer);\n    };\n    // `depositConfirmed` restarts this effect for an immediate refresh after a\n    // one-click transfer, instead of waiting for the next one-second tick.\n  }, [\n    recoveryClient,\n    hasResolvedRecipient,\n    recipient,\n    targetSymbol,\n    targetSyncPending,\n    origin,\n    depositAddressResult,\n    depositConfirmed,\n  ]);\n\n  const handleMax = React.useCallback(() => {\n    if (!origin || !balance) return;\n    setAmount(formatUnits(balance.value, origin.token.decimals));\n  }, [origin, balance, setAmount]);\n\n  // `deposit_address_shown` used to be the step-2 ENTRY anchor (fired by\n  // `goToExecute`). With no step to enter, the honest anchor is the address\n  // section actually appearing on screen. Fired at most ONCE per open -- the\n  // address can vanish and come back (origin/target change, a refetch) and a\n  // second event would inflate the funnel's own denominator; the engine\n  // unmounts on close, so the ref resets exactly when the funnel does.\n  const addressShownRef = React.useRef(false);\n  const hasDepositAddress = Boolean(depositAddress);\n  React.useEffect(() => {\n    if (!hasDepositAddress || addressShownRef.current) return;\n    addressShownRef.current = true;\n    trackDappUiFunnel(\"relay\", \"deposit_address_shown\");\n  }, [hasDepositAddress]);\n\n  // (`source_reopened` left with the Back button -- there is no step to\n  // reopen.)\n\n  // `deposit()` resolves whether or not a tx was actually submitted (it reports\n  // failures through `depositState.error`), so the success signal is a NEW tx\n  // hash appearing -- which is also exactly what `onDeposited` fires on.\n  const txHash = depositState.txHash;\n  React.useEffect(() => {\n    if (txHash) trackDappUiFunnel(\"relay\", \"deposit_submitted\");\n  }, [txHash]);\n\n  // An ENTERED amount outside [min, max] earns an inline warning -- deposits\n  // under the minimum may never be swept, and above the ceiling there may be no\n  // liquidity. It BLOCKS nothing (there is nothing left to block: the reusable\n  // address is on the same screen and the user sends what they like). The\n  // threshold is ops-controlled via the CDN apps.json\n  // (`relay.minDepositAmount`, human units, token-agnostic; read by the\n  // remoteRelayConfig hook near the top of this component) with a hardcoded\n  // fallback of 6 when the CDN value is missing or unreachable.\n  const minHuman = (() => {\n    const n = Number(remoteRelayConfig?.min);\n    return Number.isFinite(n) && n >= 0 ? String(remoteRelayConfig?.min) : \"6\";\n  })();\n  // Max is ops-set only (liquidity ceiling); no max configured = no cap.\n  const maxHuman = (() => {\n    const n = Number(remoteRelayConfig?.max);\n    return Number.isFinite(n) && n > 0 ? String(remoteRelayConfig?.max) : undefined;\n  })();\n  // Effective minimum = the larger of the ops-set CDN floor and the catalog's\n  // own per-origin minimum -- the backend won't sweep below the catalog value\n  // regardless of what ops configure.\n  const minSmallest = (() => {\n    if (!origin) return 0n;\n    const opsMin = toSmallestUnits(minHuman, origin.token.decimals);\n    const catalogMin = BigInt(origin.minAmount);\n    return opsMin > catalogMin ? opsMin : catalogMin;\n  })();\n  const minGuidanceHuman = origin ? formatUnits(minSmallest, origin.token.decimals) : minHuman;\n\n  // The un-swept balance folded into the minimum: because the backend sweeps on\n  // the COMBINED forwarder balance, the next deposit only has to cover the\n  // REMAINDER. Undefined = nothing pending (or nothing left to cover), and the\n  // notice keeps its normal lines.\n  //\n  // Decimals (CLAUDE.md §4): `pendingSweep.balance` is in the RECOVERY token's\n  // decimals (BSC USDT, 18) while `minSmallest` is in the ORIGIN token's (e.g.\n  // Base USDT, 6), so the two are rescaled to a common basis BEFORE subtracting\n  // -- entirely in BigInt, never through Number. Scaling DOWN floors, which is\n  // the safe direction: it understates the credit, so the suggested remainder is\n  // never too small to clear the minimum.\n  const pendingNotice = (() => {\n    if (!origin || !pendingSweep || minSmallest <= 0n) return undefined;\n    const originDec = origin.token.decimals;\n    const recDec = pendingSweep.decimals;\n    const pendingInOrigin =\n      originDec >= recDec\n        ? pendingSweep.balance * 10n ** BigInt(originDec - recDec)\n        : pendingSweep.balance / 10n ** BigInt(recDec - originDec);\n    // Already clears the effective minimum on its own -- that is the recovery\n    // flow's (\"execute\") case, so leave the notice alone.\n    if (pendingInOrigin >= minSmallest) return undefined;\n    const remaining = minSmallest - pendingInOrigin;\n    return {\n      pending: formatUnits(pendingSweep.balance, recDec),\n      pendingSymbol: pendingSweep.symbol,\n      remaining: formatUnits(remaining, originDec),\n    };\n  })();\n\n  const bridgeTokenOrigin =\n    catalog?.origins.find(\n      (candidate) =>\n        candidate.chainId === 56 &&\n        Boolean(\n          selectedRecovery?.token &&\n            candidate.token.address.toLowerCase() === selectedRecovery.token.toLowerCase(),\n        ),\n    ) ??\n    catalog?.origins.find(\n      (candidate) => candidate.chainId === 56 && candidate.token.symbol.toUpperCase() === \"USDT\",\n    );\n  const bridgeDecimals =\n    Number.isInteger(selectedRecovery?.decimals) && (selectedRecovery?.decimals ?? -1) >= 0\n      ? selectedRecovery!.decimals\n      : bridgeTokenOrigin?.token.decimals;\n  const bridgeSymbol =\n    selectedRecovery?.tokens?.find(\n      (token) =>\n        token.address?.toLowerCase() === selectedRecovery.token?.toLowerCase() &&\n        token.symbol,\n    )?.symbol ?? bridgeTokenOrigin?.token.symbol;\n  const latestArrivalAmount =\n    latestArrivalRaw !== undefined && bridgeDecimals !== undefined\n      ? formatUnits(latestArrivalRaw, bridgeDecimals)\n      : undefined;\n  const effectiveMinimumAtForwarder = (() => {\n    if (!origin || bridgeDecimals === undefined) return undefined;\n    const originDecimals = origin.token.decimals;\n    if (bridgeDecimals >= originDecimals) {\n      return minSmallest * 10n ** BigInt(bridgeDecimals - originDecimals);\n    }\n    const divisor = 10n ** BigInt(originDecimals - bridgeDecimals);\n    // Scaling a threshold down must round UP: rounding down could report the\n    // business minimum reached one smallest unit too early.\n    return (minSmallest + divisor - 1n) / divisor;\n  })();\n\n  const trackedOrder = trackedOrderId\n    ? matchingOrders.find((order) => order.orderId === trackedOrderId)\n    : undefined;\n  // If the modal opens while funds are already at the forwarder or bridging,\n  // show that live state even though it correctly belongs to the initial\n  // baseline. Old terminal orders are intentionally ignored; only an order\n  // detected during this open may transition to COMPLETED/FAILED here.\n  const activeOrder =\n    trackedOrder ??\n    matchingOrders.find(\n      (order) =>\n        order.status === \"BSC_FUNDED\" ||\n        order.status === \"BRIDGE_INITIATED\" ||\n        order.status === \"BRIDGE_PENDING\",\n    );\n  const activeOrderMeetsMinimum = (() => {\n    if (!activeOrder || effectiveMinimumAtForwarder === undefined) return false;\n    try {\n      return BigInt(activeOrder.amountIn) >= effectiveMinimumAtForwarder;\n    } catch {\n      return false;\n    }\n  })();\n  const recoveryMinimumReached = (() => {\n    if (!selectedRecovery || effectiveMinimumAtForwarder === undefined) return false;\n    try {\n      return (\n        BigInt(selectedRecovery.balance) > 0n &&\n        BigInt(selectedRecovery.balance) >= effectiveMinimumAtForwarder\n      );\n    } catch {\n      return false;\n    }\n  })();\n\n  const arrivalStatus: RelayWizardBundle[\"arrivalStatus\"] = (() => {\n    const common = {\n      amount: latestArrivalAmount,\n      symbol: bridgeSymbol,\n      backendStatus: activeOrder?.status,\n    };\n    if (!depositAddressResult || !ordersWatchEnabled || !watchedOrdersInitialized) {\n      return { phase: \"connecting\", ...common };\n    }\n    if (\n      ordersForRecipient?.toLowerCase() !== recipient.toLowerCase() ||\n      watchedOrdersError\n    ) {\n      return { phase: \"unavailable\", ...common };\n    }\n    if (trackedOrder?.status === \"COMPLETED\") {\n      return { phase: \"completed\", ...common, backendStatus: trackedOrder.status };\n    }\n    if (trackedOrder && isFailureStatus(trackedOrder.status)) {\n      return { phase: \"failed\", ...common, backendStatus: trackedOrder.status };\n    }\n    if (\n      activeOrder?.status === \"BRIDGE_INITIATED\" ||\n      activeOrder?.status === \"BRIDGE_PENDING\"\n    ) {\n      return { phase: \"processing\", ...common };\n    }\n    if (\n      (activeOrder?.status === \"BSC_FUNDED\" && activeOrderMeetsMinimum) ||\n      recoveryMinimumReached\n    ) {\n      return { phase: \"funded\", ...common };\n    }\n    if (trackedOrder) return { phase: \"detected\", ...common, backendStatus: trackedOrder.status };\n    return { phase: \"waiting\", ...common };\n  })();\n\n  // (The old \"reference quote for the minimum\" fetch is gone -- the notice's\n  // \"min → ~delivered\" row was cut on review 2026-07-31, so nothing needs it.)\n  const maxSmallest = origin && maxHuman ? toSmallestUnits(maxHuman, origin.token.decimals) : undefined;\n  const enteredSmallest = origin && amount.trim() ? toSmallestUnits(amount, origin.token.decimals) : 0n;\n  const belowMinimum = Boolean(origin && enteredSmallest > 0n && enteredSmallest < minSmallest);\n  const aboveMaximum = Boolean(\n    origin && maxSmallest !== undefined && enteredSmallest > 0n && enteredSmallest > maxSmallest,\n  );\n  const amountHint = (() => {\n    if (!origin) return undefined;\n    if (belowMinimum) return `Amount is below the ${minGuidanceHuman} ${origin.token.symbol} minimum.`;\n    if (aboveMaximum) return `Amount is above the ${maxHuman} ${origin.token.symbol} maximum.`;\n    return undefined;\n  })();\n\n  // One-click is opt-in (default OFF): CROSS embedded-wallet users can never\n  // sign on the origin chains, so the default modal only offers the QR /\n  // copy-address manual path. Hosts serving external wallets opt in.\n  const showOneClick = props.showOneClickDeposit ?? false;\n\n  // (The `quoteBlocks`/`canContinue` gate that lived here is gone with the\n  // Continue button: a failing or still-loading quote is now just\n  // <QuotePreview>'s own error/skeleton state next to a working deposit\n  // address, which never waited on a quote in the first place.)\n  const amountSmallestUnits =\n    origin && amount.trim() ? toSmallestUnits(amount, origin.token.decimals).toString() : undefined;\n\n  const bundle: RelayWizardBundle = {\n    activeView,\n    setActiveView,\n    recoveryOpen,\n    setRecoveryOpen,\n\n    resolvedApiBaseUrl,\n    wallet,\n\n    catalog,\n    catalogLoading,\n    catalogError,\n    origin,\n    setOrigin,\n    amount,\n    setAmount,\n    recipient,\n    setRecipient,\n    recipientValid,\n    balance,\n    balanceLoading,\n    insufficientBalance,\n    depositAddress,\n    depositAddressLoading,\n    depositAddressError,\n    arrivalStatus,\n    quote,\n    quoteLoading,\n    quoteError,\n    canDeposit,\n    deposit,\n    depositState,\n\n    targetOptions,\n    selectedTarget,\n    setSelectedTarget,\n\n    showAmountInput,\n    minNotice: origin\n      ? {\n          min: minGuidanceHuman,\n          symbol: origin.token.symbol,\n          ...pendingNotice,\n        }\n      : undefined,\n    showRecipientInput,\n    showTargetSelector,\n    showOneClick,\n    showConnectPrompt,\n    showRecipientHint,\n    canRecover,\n    recoverableCount,\n    recoveryTargetsLoading,\n    recoveryTargets,\n    recoveryOrders,\n    historyOrdersLoading: watchedOrdersLoading,\n    historyOrdersError: watchedOrdersError,\n    refreshRecovery,\n    trustedFactories: effectiveTrustedFactories,\n    amountHint,\n    amountSmallestUnits,\n    onMax: showAmountInput && balance && balance.value > 0n ? handleMax : undefined,\n  };\n\n  // Published in a LAYOUT effect, so the body (whose own subscribe/read layout\n  // effect runs after this one -- sibling fibers commit in tree order) is\n  // re-rendered before the browser paints: no empty-modal frame on open, and no\n  // blank frame after a dialog<->drawer swap either.\n  React.useLayoutEffect(() => {\n    store.publish(bundle);\n  });\n\n  // Closing (or unmounting the widget) drops the bundle. Separate effect with a\n  // stable dep so the cleanup runs ONLY on unmount, never between publishes.\n  React.useLayoutEffect(() => {\n    return () => store.publish(null);\n  }, [store]);\n\n  return null;\n}\n","// Ported from relay-protocol packages/relay-widget/src/useDepositAddress.ts\n// (v0.1.0). Mechanical substitution only: the seven wagmi hooks the original\n// consumed (useAccount / useBalance / usePublicClient / useSendTransaction /\n// useSwitchChain / useWaitForTransactionReceipt / useWriteContract) are\n// replaced by the injected `opts.wallet` functions (RelayWalletProps), and\n// viem's numeric/address helpers by ../lib/units -- dapp-ui may depend on\n// neither wagmi nor viem. The orders-list effect (original lines 642-737) now\n// lives in ./useRelayOrders, so the `orders`/`ordersLoading`/`ordersError`\n// fields are gone from this hook's result. Everything else -- the catalog\n// load, default (chain,token) select, controlled recipient default, debounced\n// amount -> quote, and the EVM one-click deposit -- is the original logic.\n//\n// Headless orchestration hook for the ADDRESS-FIRST deposit flow (Task 4).\n// Replaces useRelayOrder now that POST /v1/orders is gone: there is no\n// order-creation step any more -- a deposit address is a stable, reusable\n// per-(user, origin) resource fetched via GET /v1/deposit-address, shown\n// immediately with no amount required. Amount is entirely optional and only\n// drives a POST /v1/quote preview. The resulting orders (one per deposit\n// the sweeper detects at that address) are surfaced by polling\n// GET /v1/orders?user=.\n//\n// Ports the catalog load / default (chain,token) select / controlled\n// recipient default / debounced amount -> quote / EVM one-click deposit\n// machinery from the old useRelayOrder (frontend/src/App.tsx +\n// DepositStep.tsx lineage), re-targeted at the fetched deposit address\n// instead of a per-order one, with the create-order plumbing removed\n// entirely (no dead createOrder path).\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { createRelayClient } from '../lib/client'\nimport type { RelayClient } from '../lib/client'\nimport type { Catalog, DepositAddressResult, OriginOption, QuoteResult } from '../lib/types'\nimport { formatUnits, isAddress, parseUnits, zeroAddress } from '../lib/units'\nimport type { Hex, RelayBalance, RelayWalletProps } from '../types'\nimport { depositAddressIsForwarder, verifyForwarderFromAnyFactory } from '../lib/verifyForwarder'\nimport { useDebouncedValue } from './useDebouncedValue'\n\nconst AMOUNT_DEBOUNCE_MS = 500\n/** Standing forwarders always live on BSC, no matter which origin chain the\n * deposit comes from -- so the one-click deposit's address verification reads\n * the factory on this chain specifically (see verifyForwarderFromAnyFactory). */\nconst FORWARDER_CHAIN_ID = 56\n\n/**\n * The one origin route on which `depositAddress` IS the CREATE2 forwarder, and\n * must therefore equal the `forwarder` field of the same response (upstream\n * a8eaa22).\n *\n * The backend decides this as\n * `direct_same_token && originChainID == bscChainID && currency == bscUSDT`\n * (internal/standing/service.go): on that route it sets\n * `depositAddress = forwarder`, skipping Relay entirely. On every other route\n * `depositAddress` is Relay's own deposit address, which nothing on-chain\n * derives and no client can verify.\n *\n * Pinned here as a constant, NOT read from the API response or the catalog, for\n * the same reason `trustedFactories` is: the value decides whether a response\n * gets checked, so taking it from the response being checked would let an\n * attacker opt out of the check. Hardcoded alongside FORWARDER_CHAIN_ID, which\n * already pins this widget to BSC mainnet.\n *\n * Deployment caveat: a self-hosted backend running `direct_same_token = false`\n * (the sample configs' default) serves a Relay address for this route, so the\n * one-click deposit on it will fail closed with the verification error below.\n * Failing closed is the correct direction for a guard on where funds go; the\n * error message names this cause so an operator can act on it. The same applies\n * if a deployment changes its `bridge_dest` token away from the address below.\n */\nconst DIRECT_ROUTE = {\n  chainId: FORWARDER_CHAIN_ID,\n  /** BSC USDT -- the backend's `bridge_dest` / `bscUSDT`. */\n  token: '0x55d398326f99059fF775485246999027B3197955',\n} as const\n/** Live quotes are only guaranteed fresh for 30s -- keep refetching on that\n * cadence while the (origin, amount) inputs are unchanged. */\n/** Fallback quote refresh cadence, used only when a quote reports no `ttl` (an\n * older backend, or one with `relay.quote_ttl_seconds` unset). The server's own\n * ttl is preferred -- see quoteRefreshMs (upstream af35dd5). */\nconst QUOTE_REFRESH_FALLBACK_MS = 30_000\n/** Floor on the refresh cadence, whatever a quote's `ttl` says. A misconfigured\n * (or hostile) one-second ttl would otherwise turn every open widget into a\n * once-per-second poller against a rate-limited upstream shared with\n * GET /v1/deposit-address -- the client must not be the thing that exhausts\n * that budget on the server's say-so. */\nconst QUOTE_REFRESH_MIN_MS = 5_000\n\n/** How long to wait before refetching a quote: its own reported lifetime when\n * it has one, else the local fallback. Clamped at QUOTE_REFRESH_MIN_MS, and a\n * non-finite/non-positive ttl is ignored rather than trusted.\n *\n * Refetching ON the ttl (rather than at some fraction of it) is deliberate:\n * the ttl is how long the quote stays VALID, so refreshing when it lapses\n * replaces it exactly as it stops being true. */\nfunction quoteRefreshMs(ttl: number | undefined): number {\n  if (typeof ttl !== 'number' || !Number.isFinite(ttl) || ttl <= 0) {\n    return QUOTE_REFRESH_FALLBACK_MS\n  }\n  return Math.max(QUOTE_REFRESH_MIN_MS, ttl * 1000)\n}\n\n/** Minimal ERC-20 ABI -- only `transfer`, mirrored from frontend/src/abi/erc20.ts.\n * (balanceOf isn't needed here -- the injected `wallet.getBalance` handles\n * ERC-20 reads itself.) */\nconst ERC20_TRANSFER_ABI = [\n  {\n    type: 'function',\n    name: 'transfer',\n    stateMutability: 'nonpayable',\n    inputs: [\n      { name: 'to', type: 'address' },\n      { name: 'amount', type: 'uint256' },\n    ],\n    outputs: [{ name: '', type: 'bool' }],\n  },\n] as const\n\n/** Parses a human decimal amount into smallest units, or null if empty/invalid/zero. */\nfunction toSmallestUnits(human: string, decimals: number): bigint | null {\n  const trimmed = human.trim()\n  if (!trimmed) return null\n  try {\n    const value = parseUnits(trimmed, decimals)\n    return value > 0n ? value : null\n  } catch {\n    return null\n  }\n}\n\nexport interface DefaultToken {\n  chainId: number\n  /** Token contract address, or the zero address for the chain's native asset. */\n  address: string\n}\n\nexport type DepositPhase = 'idle' | 'switching' | 'pending' | 'confirming' | 'confirmed' | 'error'\n\nexport interface DepositState {\n  phase: DepositPhase\n  /** True while wallet.switchChain/sendTransaction/writeContract is in flight. */\n  isSubmitting: boolean\n  isConfirming: boolean\n  isConfirmed: boolean\n  /** True once the connected wallet's active chain differs from the origin chain. */\n  needsSwitch: boolean\n  txHash?: string\n  /** True when the confirmed receipt's status is \"reverted\". */\n  reverted: boolean\n  error?: string\n}\n\nexport interface UseDepositAddressOptions {\n  /** Pre-built client. Takes precedence over apiBaseUrl. */\n  client?: RelayClient\n  /** Used to build a client via createRelayClient when `client` isn't passed. */\n  apiBaseUrl?: string\n  /**\n   * Injected wallet capabilities (dapp-ui depends on neither wagmi nor viem,\n   * so the host supplies these). All optional: every one that's missing\n   * degrades gracefully -- no `walletAddress` means \"disconnected\", no\n   * `getBalance` means no balance read, and a missing\n   * submit/read/waitForReceipt function disables the one-click deposit (fail\n   * closed) rather than the whole widget.\n   */\n  wallet?: RelayWalletProps\n  /** Restrict the catalog's origins to these chain ids, in addition to the server's own curation. */\n  chains?: number[]\n  /** Controlled recipient (the CROSS \"user\" identity the deposit address/orders resolve for);\n   * when omitted, defaults to the connected wallet address. Required (directly or via a\n   * connected wallet) to fetch a deposit address at all. */\n  recipient?: string\n  /** Controlled human-decimal amount; when omitted, an internal `amount` state is used. Entirely\n   * optional -- it only drives the POST /v1/quote preview, never the deposit address fetch. */\n  amount?: string\n  /** Which (chainId, token address) to default-select once the catalog loads, instead of the first origin. */\n  defaultToken?: DefaultToken\n  /**\n   * The selected delivery target's symbol (Task 9), from `Catalog.targets` --\n   * fully controlled, mirroring `recipient`/`amount`: the caller (RelayDeposit,\n   * via useTargetTokens) owns the selection state and passes the current\n   * symbol in here. Sent as `getDepositAddress`'s `target` param only when\n   * truthy; omitted (byte-identical to pre-Task-9 behavior) when\n   * undefined/empty, in which case the backend resolves the default target.\n   * Changing this re-fetches the deposit address for the new target.\n   */\n  target?: string\n  /**\n   * Overrides the pinned factory set the one-click deposit verifies the\n   * response's forwarder against (security report L-005) -- only needed by a\n   * self-hosted deployment running its own factories. Defaults to\n   * DEFAULT_TRUSTED_FACTORIES.\n   *\n   * Deliberately NOT taken from the API: a list supplied by the same response\n   * being checked would make the check circular. See verifyForwarder.ts.\n   */\n  trustedFactories?: readonly string[]\n  onDepositAddress?: (result: DepositAddressResult) => void\n  onDeposited?: (txHash: string) => void\n  onError?: (error: Error) => void\n}\n\nexport interface UseDepositAddressResult {\n  /** Connected wallet address (from `opts.wallet.walletAddress`), or `undefined` when disconnected. */\n  address: string | undefined\n  /** Connection state -- `Boolean(wallet.walletAddress)`, exposed so callers don't re-derive it. */\n  isConnected: boolean\n\n  catalog?: Catalog\n  catalogLoading: boolean\n  catalogError?: Error\n\n  origin?: OriginOption\n  setOrigin: (origin: OriginOption) => void\n\n  recipient: string\n  setRecipient: (value: string) => void\n  /** True when `recipient` is empty (untouched) or a valid address. */\n  recipientValid: boolean\n\n  amount: string\n  setAmount: (value: string) => void\n\n  /** Connected wallet's balance of the selected origin token, or undefined (loading/disconnected/svm). */\n  balance?: RelayBalance & { formatted: string }\n  balanceLoading: boolean\n  /** True when the entered (non-debounced) amount exceeds `balance`. Always false for svm origins. */\n  insufficientBalance: boolean\n\n  /** True when the selected origin is Solana -- no balance read, no one-click deposit. */\n  isSvmOrigin: boolean\n\n  /** The reusable deposit address for (recipient, origin), fetched with no amount involved. */\n  depositAddress?: string\n  /** Full GET /v1/deposit-address response (forwarder/version metadata included). */\n  depositAddressResult?: DepositAddressResult\n  depositAddressLoading: boolean\n  depositAddressError?: Error\n\n  /** OPTIONAL amount -> quote preview. Undefined whenever `amount` is empty. */\n  quote?: QuoteResult\n  quoteLoading: boolean\n  quoteError?: Error\n\n  /** True when a one-click EVM deposit can be submitted right now (amount entered, EVM origin,\n   * connected wallet, sufficient balance, deposit address resolved, wallet able to submit,\n   * to verify the address on-chain, AND to wait for the resulting receipt -- see\n   * `walletCanDeposit` for why the receipt wait is a hard requirement, not a nicety). */\n  canDeposit: boolean\n  /** EVM one-click deposit: switch chain (if needed) then send/writeContract the entered amount\n   * to the fetched deposit address. No-op for svm origins or when no amount is entered -- callers\n   * should fall back to the manual QR/address flow in both cases. */\n  deposit: () => Promise<void>\n  depositState: DepositState\n}\n\nexport function useDepositAddress(opts: UseDepositAddressOptions = {}): UseDepositAddressResult {\n  // Keep the latest opts (callbacks especially) in a ref so effects below can\n  // read them without needing fresh-identity-triggered re-subscriptions.\n  const optsRef = useRef(opts)\n  optsRef.current = opts\n\n  const client = useMemo(() => {\n    if (opts.client) return opts.client\n    if (!opts.apiBaseUrl) {\n      throw new Error('useDepositAddress: either `client` or `apiBaseUrl` must be provided')\n    }\n    return createRelayClient({ baseUrl: opts.apiBaseUrl })\n  }, [opts.client, opts.apiBaseUrl])\n\n  const wallet = opts.wallet\n  const address = wallet?.walletAddress\n  const isConnected = Boolean(address)\n  const connectedChainId = wallet?.walletChainId\n\n  // ---- Catalog (once) -------------------------------------------------\n  const [rawCatalog, setRawCatalog] = useState<Catalog>()\n  const [catalogLoading, setCatalogLoading] = useState(true)\n  const [catalogError, setCatalogError] = useState<Error>()\n\n  useEffect(() => {\n    let cancelled = false\n    setCatalogLoading(true)\n    client\n      .getConfig()\n      .then((result) => {\n        if (cancelled) return\n        setRawCatalog(result)\n        setCatalogError(undefined)\n      })\n      .catch((err) => {\n        if (cancelled) return\n        setCatalogError(err instanceof Error ? err : new Error(String(err)))\n      })\n      .finally(() => {\n        if (!cancelled) setCatalogLoading(false)\n      })\n    return () => {\n      cancelled = true\n    }\n  }, [client])\n\n  const chains = opts.chains\n  const filteredOrigins = useMemo(() => {\n    if (!rawCatalog) return []\n    if (!chains || chains.length === 0) return rawCatalog.origins\n    const allow = new Set(chains)\n    return rawCatalog.origins.filter((o) => allow.has(o.chainId))\n  }, [rawCatalog, chains])\n\n  // The catalog exposed to callers reflects the `chains` filter, so a host\n  // building a picker from `catalog.origins` automatically respects it.\n  const catalog = useMemo(\n    () => (rawCatalog ? { ...rawCatalog, origins: filteredOrigins } : undefined),\n    [rawCatalog, filteredOrigins],\n  )\n\n  // ---- Origin selection -------------------------------------------------\n  const [origin, setOriginState] = useState<OriginOption>()\n\n  // Default-select once the (filtered) catalog loads, honoring defaultToken.\n  useEffect(() => {\n    if (origin || filteredOrigins.length === 0) return\n    const defaultToken = optsRef.current.defaultToken\n    const match = defaultToken\n      ? filteredOrigins.find(\n          (o) =>\n            o.chainId === defaultToken.chainId &&\n            o.token.address.toLowerCase() === defaultToken.address.toLowerCase(),\n        )\n      : undefined\n    setOriginState(match ?? filteredOrigins[0])\n    // optsRef.current.defaultToken is read once per catalog load, not tracked\n    // as a reactive dependency -- it only matters for the initial default.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [filteredOrigins, origin])\n\n  const [localDepositError, setLocalDepositError] = useState<string>()\n\n  // The original relied on wagmi's write hooks resetting themselves on every\n  // new write; the manual tx state below (see \"EVM one-click deposit\") has to\n  // be cleared explicitly whenever an input that a submitted tx was built\n  // from changes, or a stale hash/confirmed flag would describe the previous\n  // (origin, amount, recipient) triple. Also called at the start of every\n  // submit, which is exactly when wagmi's mutate() used to reset itself.\n  //\n  // `isConfirming` MUST be cleared here too: dropping `txHash` cancels the\n  // in-flight receipt wait through the effect's cleanup, so that wait's\n  // `.finally` no longer runs and nothing else would ever lower the flag --\n  // leaving the phase stuck at 'confirming' and canDeposit false forever.\n  const resetTxState = useCallback(() => {\n    setTxHash(undefined)\n    setIsConfirming(false)\n    setIsConfirmed(false)\n    setReverted(false)\n    setTxSubmitError(undefined)\n  }, [])\n\n  const setOrigin = useCallback(\n    (next: OriginOption) => {\n      setOriginState(next)\n      setLocalDepositError(undefined)\n      resetTxState()\n    },\n    [resetTxState],\n  )\n\n  const isSvmOrigin = origin?.kind === 'svm'\n\n  // ---- Amount (OPTIONAL -- drives only the quote preview) -------------------------------------------------\n  const [amountState, setAmountState] = useState('')\n  const amount = opts.amount !== undefined ? opts.amount : amountState\n\n  const setAmount = useCallback(\n    (value: string) => {\n      setAmountState(value)\n      setLocalDepositError(undefined)\n      resetTxState()\n    },\n    [resetTxState],\n  )\n\n  const debouncedAmount = useDebouncedValue(amount, AMOUNT_DEBOUNCE_MS)\n  const smallestAmount = origin ? toSmallestUnits(debouncedAmount, origin.token.decimals) : null\n  // Immediate (non-debounced) parse, so insufficient-balance gating and the\n  // one-click deposit's amount both react instantly rather than waiting on\n  // the quote debounce.\n  const enteredAmount = origin ? toSmallestUnits(amount, origin.token.decimals) : null\n\n  // ---- Recipient -------------------------------------------------\n  const [recipientState, setRecipientState] = useState('')\n  const [recipientTouched, setRecipientTouched] = useState(false)\n  const recipient = opts.recipient !== undefined ? opts.recipient : recipientState\n\n  const setRecipient = useCallback(\n    (value: string) => {\n      setRecipientState(value)\n      setRecipientTouched(true)\n      setLocalDepositError(undefined)\n      resetTxState()\n    },\n    [resetTxState],\n  )\n\n  // Default the recipient to the connected wallet, unless the caller controls\n  // it (opts.recipient) or the user has already edited it locally.\n  useEffect(() => {\n    if (opts.recipient !== undefined) return\n    if (address && !recipientTouched) {\n      setRecipientState(address)\n    }\n  }, [address, recipientTouched, opts.recipient])\n\n  const recipientValid = recipient.length === 0 || isAddress(recipient)\n  // A valid, non-empty recipient -- the key that gates the deposit address\n  // fetch (and, in the caller, the orders-list poll).\n  const recipientKey = recipient.length > 0 && isAddress(recipient) ? recipient : undefined\n\n  // ---- Balance -------------------------------------------------\n  // Skipped for svm origins -- the token address is base58, not a 20-byte\n  // EVM address, so the connected EVM wallet can't read it.\n  const [balance, setBalance] = useState<(RelayBalance & { formatted: string }) | undefined>()\n  const [balanceLoading, setBalanceLoading] = useState(false)\n  // bump to refetch after a confirmed deposit\n  const [balanceNonce, setBalanceNonce] = useState(0)\n  // NOTE for hosts: the injected functions must be STABLE references\n  // (useCallback/useMemo, or a module-level wallet adapter). This effect and\n  // the receipt-wait effect below key off their identity, so a fresh inline\n  // arrow on every render would re-fire them on every render.\n  const getBalance = wallet?.getBalance\n\n  useEffect(() => {\n    if (!getBalance || !address || !origin || isSvmOrigin) {\n      setBalance(undefined)\n      setBalanceLoading(false)\n      return\n    }\n    let cancelled = false\n    setBalanceLoading(true)\n    const token = origin.token.address.toLowerCase() === zeroAddress ? undefined : (origin.token.address as Hex)\n    getBalance({ chainId: origin.chainId, address: address as Hex, token })\n      .then((b) => {\n        if (cancelled) return\n        setBalance({ ...b, formatted: formatUnits(b.value, b.decimals) })\n      })\n      .catch(() => {\n        if (!cancelled) setBalance(undefined)\n      })\n      .finally(() => {\n        if (!cancelled) setBalanceLoading(false)\n      })\n    return () => {\n      cancelled = true\n    }\n  }, [getBalance, address, origin, isSvmOrigin, balanceNonce])\n\n  const balanceValue = balance?.value ?? null\n\n  const insufficientBalance = Boolean(\n    !isSvmOrigin && enteredAmount !== null && balanceValue !== null && enteredAmount > balanceValue,\n  )\n\n  // ---- Deposit address (no amount involved) -------------------------------------------------\n  const [depositAddressResult, setDepositAddressResult] = useState<DepositAddressResult>()\n  const [depositAddressLoading, setDepositAddressLoading] = useState(false)\n  const [depositAddressError, setDepositAddressError] = useState<Error>()\n\n  const originChainId = origin?.chainId\n  const originCurrency = origin?.token.address\n  // Fully controlled (Task 9) -- see UseDepositAddressOptions.target's doc.\n  const targetSymbol = opts.target\n\n  useEffect(() => {\n    if (!recipientKey || !originChainId || !originCurrency) {\n      setDepositAddressResult(undefined)\n      setDepositAddressError(undefined)\n      setDepositAddressLoading(false)\n      return\n    }\n\n    let cancelled = false\n    // Clear any previously-held address synchronously as the key inputs\n    // change: a standing forwarder's deposit address is genuinely per-origin\n    // (per recipient/chain/token), so the old origin's address is WRONG for\n    // the new one -- not merely stale. Leaving it in place would let\n    // DepositStep render the old address with the new origin's\n    // chain/kind/currency (a QR/payment-URI mismatch), and could let a\n    // one-click send target the wrong-chain address. Blank it until the\n    // fresh fetch resolves.\n    setDepositAddressResult(undefined)\n    setDepositAddressError(undefined)\n    setDepositAddressLoading(true)\n    // `target` is only appended as a second arg when truthy -- an\n    // empty/undefined target must call getDepositAddress with the EXACT same\n    // single-argument shape as pre-Task-9, so byte-identical behavior (and\n    // the existing toHaveBeenCalledWith assertions) hold when the host never\n    // opts into target selection.\n    const request = targetSymbol\n      ? client.getDepositAddress({ user: recipientKey, originChainId, originCurrency }, targetSymbol)\n      : client.getDepositAddress({ user: recipientKey, originChainId, originCurrency })\n    request\n      .then((result) => {\n        if (cancelled) return\n        setDepositAddressResult(result)\n        setDepositAddressError(undefined)\n        optsRef.current.onDepositAddress?.(result)\n      })\n      .catch((err) => {\n        if (cancelled) return\n        setDepositAddressResult(undefined)\n        const e = err instanceof Error ? err : new Error(String(err))\n        setDepositAddressError(e)\n        optsRef.current.onError?.(e)\n      })\n      .finally(() => {\n        if (!cancelled) setDepositAddressLoading(false)\n      })\n    return () => {\n      cancelled = true\n    }\n  }, [client, recipientKey, originChainId, originCurrency, targetSymbol])\n\n  // ---- Deposit-intent change -> drop the previous tx state -------------------------------------------------\n  // The setOrigin/setAmount/setRecipient setters each reset the tx state, but\n  // they only cover the UNCONTROLLED paths: `target`, `amount` and `recipient`\n  // are all fully controllable props, and a host changing any of them never\n  // goes through a setter here. Without this effect a confirmed banner (and the\n  // canDeposit=false that comes with it) would sit under a deposit address\n  // fetched for the NEW target/recipient -- describing a tx that was sent\n  // somewhere else entirely.\n  //\n  // The key is everything a submitted tx was built from: the origin\n  // (chain+token), the identity the address was minted for, the delivery\n  // target, and the amount. `amount` belongs in it precisely because a\n  // controlled amount change must reset too -- and including it costs the\n  // uncontrolled path nothing, since setAmount already resets.\n  //\n  // Compared against the PREVIOUS key rather than tracked as a plain dep, so\n  // the body runs only on a real change: the initial mount is skipped (the ref\n  // starts at the first key), and a resubmit for the SAME key -- deposit()\n  // setting isTxPending/txHash while nothing about the intent moved -- cannot\n  // be clobbered mid-flight, which is what keeps the \"reset at the start of a\n  // resubmit, not when the new hash lands\" behavior intact.\n  const depositIntentKey = [\n    originChainId ?? '',\n    (originCurrency ?? '').toLowerCase(),\n    (recipientKey ?? '').toLowerCase(),\n    targetSymbol ?? '',\n    amount.trim(),\n  ].join('|')\n  const lastDepositIntentKeyRef = useRef(depositIntentKey)\n\n  useEffect(() => {\n    if (lastDepositIntentKeyRef.current === depositIntentKey) return\n    lastDepositIntentKeyRef.current = depositIntentKey\n    setLocalDepositError(undefined)\n    resetTxState()\n  }, [depositIntentKey, resetTxState])\n\n  // ---- Live quote (debounced, 30s refresh) -- OPTIONAL preview only -------------------------------------------------\n  const [quote, setQuote] = useState<QuoteResult>()\n  const [quoteLoading, setQuoteLoading] = useState(false)\n  const [quoteError, setQuoteError] = useState<Error>()\n\n  const smallestAmountKey = smallestAmount?.toString()\n\n  useEffect(() => {\n    if (!originChainId || !originCurrency || !smallestAmountKey) {\n      setQuote(undefined)\n      setQuoteError(undefined)\n      setQuoteLoading(false)\n      return\n    }\n\n    let cancelled = false\n    let timer: ReturnType<typeof setTimeout> | undefined\n    // Only the FIRST fetch for this (originChainId, originCurrency, amount)\n    // key should flip `quoteLoading` -- background 30s refreshes for the same\n    // key update `quote` in place once the new result arrives, but must not\n    // toggle loading back on, or a quote-preview UI (which hides the existing\n    // quote while loading) would flicker to a spinner every refresh even\n    // though a perfectly good quote is still on screen.\n    let isFirstFetch = true\n\n    async function fetchQuote() {\n      if (isFirstFetch) setQuoteLoading(true)\n      // The cadence for the NEXT refetch comes from the quote we are about to\n      // receive, so it is captured per-iteration rather than closed over once.\n      // A failed fetch leaves it at the fallback: there is no fresh ttl to\n      // believe, and retrying on the last good quote's lifetime would be\n      // pacing off a quote that no longer exists.\n      let nextDelay = QUOTE_REFRESH_FALLBACK_MS\n      try {\n        const result = await client.getQuote({\n          originChainId: originChainId as number,\n          originCurrency: originCurrency as string,\n          amount: smallestAmountKey as string,\n        })\n        if (cancelled) return\n        setQuote(result)\n        setQuoteError(undefined)\n        nextDelay = quoteRefreshMs(result.ttl)\n      } catch (err) {\n        if (cancelled) return\n        setQuote(undefined)\n        setQuoteError(err instanceof Error ? err : new Error(String(err)))\n      } finally {\n        if (!cancelled) setQuoteLoading(false)\n        isFirstFetch = false\n      }\n      if (!cancelled) {\n        timer = setTimeout(fetchQuote, nextDelay)\n      }\n    }\n\n    fetchQuote()\n\n    return () => {\n      cancelled = true\n      if (timer) clearTimeout(timer)\n    }\n  }, [client, originChainId, originCurrency, smallestAmountKey])\n\n  // ---- EVM one-click deposit (ported from the old useRelayOrder), targeting\n  // the fetched depositAddress instead of a per-order one. Only meaningful\n  // when an amount has been entered -- an amount-less \"send anything\" deposit\n  // has nothing for a wallet tx to carry, so callers must fall back to the\n  // manual QR/address flow when `canDeposit` is false. -------------------------------------------------\n  // The original expressed all of this declaratively through wagmi's\n  // useSwitchChain/useSendTransaction/useWriteContract/useWaitForTransactionReceipt;\n  // with injected async functions the same lifecycle is held in local state.\n  const [isSwitching, setIsSwitching] = useState(false)\n  const [isTxPending, setIsTxPending] = useState(false)\n  const [txHash, setTxHash] = useState<Hex | undefined>()\n  const [txSubmitError, setTxSubmitError] = useState<string | undefined>()\n\n  const [isConfirming, setIsConfirming] = useState(false)\n  const [isConfirmed, setIsConfirmed] = useState(false)\n  const [reverted, setReverted] = useState(false)\n  const waitForReceipt = wallet?.waitForReceipt\n\n  // onDeposited fires only for a CONFIRMED, non-reverted deposit, once per tx\n  // (upstream 83845fb). It used to fire as soon as a tx hash existed -- on\n  // submission, before the receipt was known -- so a deposit that later\n  // reverted still reported itself as completed. The callback's name is a\n  // claim about settlement and integrators act on it (crediting a balance,\n  // advancing a checkout step), so reporting an unconfirmed or reverted\n  // transfer is a correctness bug, not a timing nicety. Callers that want the\n  // earlier \"submitted\" signal have depositState.phase / depositState.txHash.\n  const notifiedHashRef = useRef<string | undefined>(undefined)\n\n  useEffect(() => {\n    if (!txHash || !origin || !waitForReceipt) return\n    let cancelled = false\n    setIsConfirming(true)\n    setIsConfirmed(false)\n    setReverted(false)\n    waitForReceipt({ chainId: origin.chainId, hash: txHash })\n      .then((receipt) => {\n        if (cancelled) return\n        setIsConfirmed(true)\n        setReverted(receipt.status === 'reverted')\n        setBalanceNonce((n) => n + 1)\n        if (receipt.status !== 'reverted' && txHash !== notifiedHashRef.current) {\n          notifiedHashRef.current = txHash\n          optsRef.current.onDeposited?.(txHash)\n        }\n      })\n      .catch((err) => {\n        if (!cancelled) setTxSubmitError(err instanceof Error ? err.message : String(err))\n      })\n      .finally(() => {\n        if (!cancelled) setIsConfirming(false)\n      })\n    return () => {\n      cancelled = true\n    }\n    // origin intentionally NOT tracked: the receipt belongs to the chain the tx\n    // was sent on; origin changes mid-wait must not retarget the wait.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [txHash, waitForReceipt])\n\n  const needsSwitch = connectedChainId !== undefined && origin !== undefined && connectedChainId !== origin.chainId\n  const isSubmitting = isSwitching || isTxPending\n  const submitError = localDepositError ?? txSubmitError\n\n  let depositPhase: DepositPhase = 'idle'\n  if (submitError) depositPhase = 'error'\n  else if (isConfirmed) depositPhase = 'confirmed'\n  else if (isConfirming) depositPhase = 'confirming'\n  else if (isSubmitting) depositPhase = isSwitching ? 'switching' : 'pending'\n\n  const depositState: DepositState = {\n    phase: depositPhase,\n    isSubmitting,\n    isConfirming,\n    isConfirmed,\n    needsSwitch,\n    txHash,\n    reverted: isConfirmed && reverted,\n    error: submitError,\n  }\n\n  // Defense-in-depth against a stale/mismatched address (consistent with this\n  // codebase's CREATE2/address-mismatch guarding style): only allow a\n  // one-click deposit when the held address was actually fetched for the\n  // currently-selected origin. Combined with the synchronous clear above this\n  // is belt-and-suspenders, but it guarantees a send can never target an\n  // address minted for a different chain/token.\n  const addressMatchesOrigin = Boolean(\n    origin &&\n      depositAddressResult &&\n      depositAddressResult.originChainId === origin.chainId &&\n      depositAddressResult.originCurrency.toLowerCase() === origin.token.address.toLowerCase(),\n  )\n\n  // Every capability the one-click path needs: both submit paths (the branch\n  // taken depends on the origin token), the on-chain read the forwarder\n  // verification is built on, AND the receipt wait that owns the post-send\n  // lifecycle. Missing `readContract` means the address cannot be verified, and\n  // an unverifiable address is exactly the case where a send must not proceed\n  // -- so the button is disabled rather than the check skipped (fail closed,\n  // matching verifyForwarder.ts).\n  //\n  // `waitForReceipt` is part of the same quartet for a duplicate-send reason,\n  // not a cosmetic one: it is the ONLY thing that raises `isConfirming` after a\n  // successful send. Without it a submitted deposit leaves `isTxPending` false,\n  // `isConfirming` false and `isConfirmed` false, so the phase drops straight\n  // back to 'idle' and every canDeposit term is satisfied again -- an armed\n  // button sitting beside a live tx hash, one click away from transferring the\n  // funds twice. A host that cannot wait for receipts gets the QR/address flow\n  // instead.\n  const walletCanDeposit = Boolean(\n    wallet?.sendTransaction && wallet?.writeContract && wallet?.readContract && wallet?.waitForReceipt,\n  )\n\n  const canDeposit = Boolean(\n    isConnected &&\n      walletCanDeposit &&\n      origin &&\n      !isSvmOrigin &&\n      depositAddressResult &&\n      addressMatchesOrigin &&\n      enteredAmount &&\n      enteredAmount > 0n &&\n      !insufficientBalance &&\n      !isSubmitting &&\n      !isConfirming,\n  )\n\n  const deposit = useCallback(async () => {\n    if (isSvmOrigin) return // display-only -- no one-click for svm origins.\n    if (!origin || !depositAddressResult) return\n    // No injected submit path -- nothing to send with, so this is a no-op and\n    // the caller's manual QR/address flow is the only route.\n    if (!wallet?.sendTransaction || !wallet?.writeContract) return\n\n    setLocalDepositError(undefined)\n    const amountWei = toSmallestUnits(amount, origin.token.decimals)\n    if (!amountWei || amountWei <= 0n) {\n      setLocalDepositError('enter an amount to use the one-click deposit')\n      return\n    }\n\n    const isNative = origin.token.address.toLowerCase() === zeroAddress\n    const targetAddress = depositAddressResult.depositAddress\n\n    // Verify what the response claims before sending funds anywhere (security\n    // report L-005). Scope, stated honestly:\n    //\n    //   * The `forwarder` in this same response IS verifiable, against a pinned\n    //     factory and the chain -- so a response tampered in that field is\n    //     caught here rather than after the money has moved.\n    //   * On a bridged route `depositAddress` is RELAY's open deposit address.\n    //     It is not a CREATE2 address of ours and nothing on-chain derives it,\n    //     so no client-side computation can confirm it. That residual is\n    //     documented in verifyForwarder.ts, not papered over.\n    //   * On the direct BSC route the deposit address IS the forwarder, so the\n    //     check below covers the exact address receiving the funds.\n    //\n    // Fails closed, and only for the one-click path: a user reading the address\n    // off-screen is unaffected, so this cannot lock anyone out of depositing.\n    //\n    // The forwarder always lives on BSC, whatever origin chain the user is\n    // depositing from, so the read is pinned to FORWARDER_CHAIN_ID rather than\n    // the origin chain (see verifyForwarderFromAnyFactory).\n    const verdict = await verifyForwarderFromAnyFactory(wallet?.readContract, FORWARDER_CHAIN_ID, {\n      forwarder: depositAddressResult.forwarder,\n      // `recipientKey`, not a response field -- this is what proves the\n      // forwarder is derived for the identity the deposit is FOR. It must be\n      // the same value the address was fetched with (see the deposit-address\n      // effect above), which is the recipient, NOT necessarily the connected\n      // wallet: with a controlled `recipient` prop (or a recipient typed into\n      // SourceForm) the two differ, and verifying against the wallet would\n      // mismatch on every such deposit. Mirrors useRecovery.ts's own\n      // recipientKey-based verification.\n      recipient: recipientKey,\n      version: depositAddressResult.version,\n      trusted: optsRef.current.trustedFactories,\n    })\n    if (!verdict.ok) {\n      setLocalDepositError(`address verification failed: ${verdict.reason}`)\n      return\n    }\n\n    // Bind the VERIFIED forwarder to the address actually receiving the funds\n    // (upstream a8eaa22).\n    //\n    // The verification above proves `depositAddressResult.forwarder` is derived\n    // from a pinned factory for the recipient. It says nothing about\n    // `depositAddress`, which is the separate field this function transfers to.\n    // On the direct route those two are the same address (the backend assigns\n    // `depositAddress = forwarder`), so a response that keeps a valid\n    // `forwarder` while substituting `depositAddress` would otherwise pass every\n    // check and send the funds to the substituted address.\n    //\n    // Scoped to DIRECT_ROUTE deliberately. On a bridged route `depositAddress`\n    // is Relay's address and legitimately differs from the forwarder --\n    // requiring equality there would reject real deposits (prod carries a\n    // bridged BSC/USDC origin on this very chain, so a chain-id-only test would\n    // do exactly that).\n    const isDirectRoute =\n      origin.chainId === DIRECT_ROUTE.chainId &&\n      origin.token.address.toLowerCase() === DIRECT_ROUTE.token.toLowerCase()\n    if (isDirectRoute && !depositAddressIsForwarder(targetAddress, depositAddressResult.forwarder)) {\n      setLocalDepositError(\n        'address verification failed: deposit address does not match the verified forwarder ' +\n          \"(expected them to be identical on this route; check the backend's direct_same_token setting)\",\n      )\n      return\n    }\n\n    try {\n      if (needsSwitch) {\n        if (!wallet?.switchChain) throw new Error('wallet cannot switch chains')\n        setIsSwitching(true)\n        try {\n          await wallet.switchChain(origin.chainId)\n        } finally {\n          setIsSwitching(false)\n        }\n      }\n      // Same reset wagmi's mutate() performed at the start of every write:\n      // without it a resubmit would keep showing the PREVIOUS deposit's hash\n      // and 'confirmed' phase for the whole signing window.\n      resetTxState()\n      setIsTxPending(true)\n      try {\n        const hash = isNative\n          ? await wallet.sendTransaction({ chainId: origin.chainId, to: targetAddress as Hex, value: amountWei })\n          : await wallet.writeContract({\n              chainId: origin.chainId,\n              address: origin.token.address as Hex,\n              abi: ERC20_TRANSFER_ABI,\n              functionName: 'transfer',\n              args: [targetAddress as Hex, amountWei],\n            })\n        setTxHash(hash)\n      } finally {\n        setIsTxPending(false)\n      }\n    } catch (err) {\n      setLocalDepositError((err as Error).message)\n    }\n  }, [isSvmOrigin, origin, depositAddressResult, amount, needsSwitch, wallet, recipientKey, resetTxState])\n\n  return {\n    address,\n    isConnected,\n\n    catalog,\n    catalogLoading,\n    catalogError,\n\n    origin,\n    setOrigin,\n\n    recipient,\n    setRecipient,\n    recipientValid,\n\n    amount,\n    setAmount,\n\n    balance,\n    balanceLoading,\n    insufficientBalance,\n\n    isSvmOrigin,\n\n    depositAddress: depositAddressResult?.depositAddress,\n    depositAddressResult,\n    depositAddressLoading,\n    depositAddressError,\n\n    quote,\n    quoteLoading,\n    quoteError,\n\n    canDeposit,\n    deposit,\n    depositState,\n  }\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Ported verbatim from frontend/src/hooks/useDebouncedValue.ts -- no\n// behavior changes, just relocated into the framework-agnostic-consumer\n// package so useDepositAddress can debounce the live-quote inputs.\nimport { useEffect, useState } from 'react'\n\n/** Returns `value`, delayed by `delayMs` after it last changed. */\nexport function useDebouncedValue<T>(value: T, delayMs: number): T {\n  const [debounced, setDebounced] = useState(value)\n\n  useEffect(() => {\n    const timer = setTimeout(() => setDebounced(value), delayMs)\n    return () => clearTimeout(timer)\n  }, [value, delayMs])\n\n  return debounced\n}\n","// Ported from relay-protocol packages/relay-widget/src/useDepositAddress.ts\n// (lines 642-737, plus the optsRef pattern at lines 208-211 and the\n// ORDERS_FALLBACK_POLL_MS / ORDERS_DEBOUNCE_MS constants at lines 41-49,\n// v0.1.0). Extracted into a standalone hook (Task 5) so both\n// RelayDepositContent (gated on catalog via `enabled`) and RelayHistory\n// (always enabled) can consume the orders SSE + fallback-polling machinery\n// without depending on the rest of useDepositAddress.\nimport { useEffect, useRef, useState } from 'react'\nimport type { RelayClient } from '../lib/client'\nimport type { OrderSummary } from '../lib/types'\nimport { isAddress } from '../lib/units'\n\n/** Safety-net poll cadence for the orders list (GET /v1/orders?user=) -- the\n * primary refresh signal is the SSE stream (GET /v1/orders/stream?user=),\n * so this only matters when that stream is unavailable or has silently\n * dropped. */\nconst ORDERS_FALLBACK_POLL_MS = 30_000\n/** Coalesces bursts of `orders-changed` stream events (or an event\n * immediately followed by a reconnect) into a single trailing `listOrders`\n * refetch. */\nconst ORDERS_DEBOUNCE_MS = 300\n\nexport interface UseRelayOrdersOptions {\n  client: RelayClient\n  /** Valid EVM address, or undefined -- undefined clears the list and stops polling. */\n  recipient?: string\n  /** Gate: wait for truthy before the first fetch (RelayDeposit passes rawCatalog). Default true. */\n  enabled?: boolean\n  /** Keep the snapshot live with SSE + fallback polling. Default true. */\n  live?: boolean\n  /** REST safety-net cadence in milliseconds. Defaults to 30s; the open Deposit popup uses 1s\n   * so completion still appears promptly when the SSE event is absent. */\n  refetchInterval?: number\n  /** @deprecated Use `refetchInterval`. Retained for compatibility. */\n  fallbackPollMs?: number\n  onError?: (e: Error) => void\n}\n\nexport interface UseRelayOrdersResult {\n  /** Every order swept from deposits to this recipient so far, newest-first\n   * as served by the API. Kept live unless `live: false` requests one snapshot. */\n  orders: OrderSummary[]\n  ordersLoading: boolean\n  ordersError?: Error\n  /** Recipient the current snapshot belongs to. Undefined while a new\n   * subscription is waiting for its first accepted fetch, so consumers never\n   * baseline a previous recipient's rows as fresh deposits. */\n  ordersForRecipient?: string\n  /** Increments after every accepted fetch, even when the client returns the\n   * same array reference or the request fails. Consumers use this as a\n   * low-frequency refresh signal for related read models such as /recovery. */\n  ordersRevision: number\n  /** True once at least one fetch has COMPLETED (success or failure) for the\n   * current (recipient, enabled) subscription; false again when it resets.\n   * Distinct from `!ordersLoading`, which is also true BEFORE the first fetch\n   * has even started -- consumers that snapshot the list (e.g. the deposit\n   * wizard's new-order watch) must wait for this, or they baseline against\n   * the initial empty state and misread every existing order as new. */\n  ordersInitialized: boolean\n}\n\nexport function useRelayOrders(opts: UseRelayOrdersOptions): UseRelayOrdersResult {\n  // Keep the latest opts (callbacks especially) in a ref so the effect below\n  // can read them without needing fresh-identity-triggered re-subscriptions.\n  const optsRef = useRef(opts)\n  optsRef.current = opts\n\n  const { client, enabled = true, live = true } = opts\n  const refetchInterval = opts.refetchInterval ?? opts.fallbackPollMs ?? ORDERS_FALLBACK_POLL_MS\n  const recipientKey = opts.recipient && isAddress(opts.recipient) ? opts.recipient : undefined\n\n  // ---- Orders list: SSE-driven refresh, 30s fallback poll -------------------------------------------------\n  // One order per deposit the sweeper detects at the standing address, so\n  // this refreshes continuously (not scoped to a single order) as long as\n  // `recipient` resolves to a valid address -- independent of whether a\n  // deposit address has been fetched yet for the *current* origin.\n  //\n  // GET /v1/orders/stream?user= is a minimal-signal SSE endpoint: it emits\n  // `event: connected` on open, `event: orders-changed` whenever this user's\n  // orders change, and `: heartbeat` comments -- it never pushes order data\n  // itself, so every signal (initial connect, a change event, or a\n  // reconnect after a drop) just triggers a normal `listOrders` refetch. The\n  // 30s fallback interval is a safety net for a silently-dropped stream and\n  // the only refresh mechanism in environments without `EventSource` (SSR,\n  // some test runners) -- it replaces the old 3s poll cadence entirely.\n  const [orders, setOrders] = useState<OrderSummary[]>([])\n  const [ordersLoading, setOrdersLoading] = useState(false)\n  const [ordersError, setOrdersError] = useState<Error>()\n  const [ordersInitialized, setOrdersInitialized] = useState(false)\n  const [ordersForRecipient, setOrdersForRecipient] = useState<string>()\n  const [ordersRevision, setOrdersRevision] = useState(0)\n\n  useEffect(() => {\n    if (!recipientKey) {\n      setOrders([])\n      setOrdersError(undefined)\n      setOrdersLoading(false)\n      setOrdersInitialized(false)\n      setOrdersForRecipient(undefined)\n      setOrdersRevision(0)\n      return\n    }\n\n    // Wait for the gate (RelayDeposit's rawCatalog) before firing the FIRST\n    // fetch -- this is a short, one-time wait, not a dependency on the\n    // *current* origin.\n    if (!enabled) {\n      setOrders([])\n      setOrdersError(undefined)\n      setOrdersLoading(false)\n      setOrdersInitialized(false)\n      setOrdersForRecipient(undefined)\n      setOrdersRevision(0)\n      return\n    }\n\n    let cancelled = false\n    let isFirstFetch = true\n    let debounceTimer: ReturnType<typeof setTimeout> | undefined\n\n    // Monotonic request-sequence guard: the initial fetch, stream-triggered\n    // debounced refetches, and the fallback-poll interval can all have\n    // `listOrders` calls in flight at once, and there is no guarantee they\n    // resolve in the order they were issued. Each `refetch()` call captures\n    // the seq it was issued at; if a newer call has been issued by the time\n    // this one resolves, its (now-stale) result is dropped instead of\n    // clobbering the fresher data already applied.\n    let seq = 0\n    let requestsInFlight = 0\n\n    async function refetch() {\n      const mySeq = ++seq\n      requestsInFlight += 1\n      if (isFirstFetch) setOrdersLoading(true)\n      try {\n        const result = await client.listOrders(recipientKey as string)\n        if (cancelled) return\n        if (mySeq === seq) {\n          setOrders(result)\n          setOrdersError(undefined)\n        }\n      } catch (err) {\n        if (cancelled) return\n        const e = err instanceof Error ? err : new Error(String(err))\n        if (mySeq === seq) {\n          setOrdersError(e)\n          optsRef.current.onError?.(e)\n        }\n      } finally {\n        requestsInFlight -= 1\n        // A reconnect-triggered request can overtake the initial fetch. Only\n        // the latest issued request may publish loading/initialization state;\n        // otherwise the stale initial request briefly marks an empty snapshot\n        // initialized and a deposit watcher treats every existing order as new.\n        if (!cancelled && mySeq === seq) {\n          setOrdersLoading(false)\n          // Success OR failure both count: \"a fetch has completed\" is the\n          // fact consumers wait on, and a failure is reported via ordersError.\n          setOrdersInitialized(true)\n          setOrdersForRecipient(recipientKey)\n          setOrdersRevision((value) => value + 1)\n        }\n        isFirstFetch = false\n      }\n    }\n\n    // Coalesces bursts of stream signals (several orders changing at once,\n    // or a signal immediately followed by a reconnect) into a single\n    // trailing `listOrders` call instead of one per event.\n    function debouncedRefetch() {\n      if (debounceTimer) clearTimeout(debounceTimer)\n      debounceTimer = setTimeout(() => {\n        debounceTimer = undefined\n        if (!cancelled) refetch()\n      }, ORDERS_DEBOUNCE_MS)\n    }\n\n    refetch() // Unchanged first-fetch / ordersLoading semantics -- fires immediately, not debounced.\n\n    let eventSource: EventSource | undefined\n    if (live && typeof EventSource !== 'undefined') {\n      eventSource = new EventSource(client.ordersStreamUrl(recipientKey))\n      eventSource.addEventListener('orders-changed', debouncedRefetch)\n      // Fires on the initial connect AND every auto-reconnect after a drop --\n      // either way, a snapshot refetch covers any change that happened while\n      // disconnected (or in the brief window before the stream was open).\n      eventSource.onopen = () => {\n        debouncedRefetch()\n      }\n    }\n\n    // Safety net: refetches on a fixed cadence regardless of stream health,\n    // and is the ONLY refresh mechanism when `EventSource` isn't available.\n    const fallbackTimer =\n      live && Number.isFinite(refetchInterval) && refetchInterval > 0\n        ? setInterval(() => {\n            // SSE invalidations may deliberately overtake an old request; the\n            // safety poll has no such urgency, so do not add another request\n            // while any snapshot fetch is still running.\n            if (requestsInFlight === 0) refetch()\n          }, refetchInterval)\n        : undefined\n\n    return () => {\n      cancelled = true\n      if (debounceTimer) clearTimeout(debounceTimer)\n      if (fallbackTimer) clearInterval(fallbackTimer)\n      eventSource?.close()\n    }\n  }, [client, recipientKey, enabled, live, refetchInterval])\n\n  return {\n    orders,\n    ordersLoading,\n    ordersError,\n    ordersInitialized,\n    ordersForRecipient,\n    ordersRevision,\n  }\n}\n","// Ported from relay-protocol packages/relay-widget (v0.1.0).\n// Headless selection state for the user-selectable CROSS delivery token\n// (Task 9). Consumes Task 8's `Catalog.targets` -- this hook does NOT fetch\n// anything itself, it purely derives `options` (the catalog intersected with\n// an optional host-supplied `filter`) and manages which one is `selected`,\n// so <RelayDeposit> can pass `selected.symbol` into useDepositAddress ->\n// client.getDepositAddress's `target` param.\nimport { useMemo, useState } from 'react'\nimport type { Target } from '../lib/types'\n\nexport interface UseTargetTokensOptions {\n  /** The full set of delivery targets from `Catalog.targets` (Task 8). */\n  catalogTargets: Target[]\n  /**\n   * Restrict `options` to these symbols (a host-supplied allowlist, e.g. the\n   * RelayDeposit `targetTokens` prop). Absent or empty -- ALL catalog targets\n   * are offered, preserving catalog order. Any filter symbol absent from the\n   * catalog is silently ignored (never throws, never adds a phantom option).\n   */\n  filter?: string[]\n  /**\n   * Symbol to select initially, e.g. the RelayDeposit `defaultTarget` prop.\n   * Falls back gracefully (never throws) when the symbol isn't present in\n   * `options`: the catalog's own default (`isDefault: true`) wins next, then\n   * the first option, then `undefined` when `options` is empty.\n   */\n  defaultTarget?: string\n}\n\nexport interface UseTargetTokensResult {\n  /** `catalogTargets` intersected with `filter`, in catalog order. */\n  options: Target[]\n  selected: Target | undefined\n  /** Updates the selection. A symbol not present in `options` is ignored. */\n  setSelected: (symbol: string) => void\n}\n\nfunction pickInitial(options: Target[], defaultTarget?: string): Target | undefined {\n  if (defaultTarget) {\n    const match = options.find((t) => t.symbol === defaultTarget)\n    if (match) return match\n  }\n  return options.find((t) => t.isDefault) ?? options[0]\n}\n\nexport function useTargetTokens({\n  catalogTargets,\n  filter,\n  defaultTarget,\n}: UseTargetTokensOptions): UseTargetTokensResult {\n  const options = useMemo(() => {\n    if (!filter || filter.length === 0) return catalogTargets\n    const allow = new Set(filter)\n    return catalogTargets.filter((t) => allow.has(t.symbol))\n    // `catalogTargets` is expected to be a stable reference per catalog load\n    // (see Catalog.targets / getConfig) -- re-deriving on every render of an\n    // inline array literal is intentionally avoided by callers, not guarded\n    // against here.\n  }, [catalogTargets, filter])\n\n  const [selectedSymbol, setSelectedSymbol] = useState<string | undefined>(\n    () => pickInitial(options, defaultTarget)?.symbol,\n  )\n\n  // Resolve the current selection against `options` every render (rather than\n  // storing the Target object in state) so a later catalog/filter change that\n  // drops the previously-selected symbol falls back gracefully instead of\n  // holding a stale Target no longer in `options`.\n  const selected =\n    (selectedSymbol !== undefined ? options.find((t) => t.symbol === selectedSymbol) : undefined) ??\n    pickInitial(options, defaultTarget)\n\n  function setSelected(symbol: string) {\n    if (!options.some((t) => t.symbol === symbol)) return\n    setSelectedSymbol(symbol)\n  }\n\n  return { options, selected, setSelected }\n}\n","// Environment-based default relay-protocol orchestrator API base URLs\n// (2026-07-30 user decision superseding the original design's \"apiBaseUrl is\n// required\" rule -- see docs/superpowers/specs/2026-07-29-relay-deposit-dapp-ui-design.md\n// §13). An explicit `apiBaseUrl` (or a pre-built `client`, checked by the\n// callers of this module) still takes precedence; this is only the fallback\n// for hosts that pass `environment` instead.\n//\n// Static `Record<Environment, string>` map, mirroring lib/remote-config.ts's\n// naming convention (`RELAY_API_BASE_URLS`, singular-capability lookup) --\n// but as a hardcoded map rather than a CDN fetch, since relay's backend origin\n// is a fixed set of three known URLs, not remote config that changes without\n// a redeploy.\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport type { Environment } from \"../../../types\";\n\nexport const RELAY_API_BASE_URLS: Record<Environment, string> = {\n  dev: \"https://dev-relay-protocol-api.onechain.nexus\",\n  // stage points at the dev orchestrator for now (2026-07-30 user decision):\n  // a stage relay backend isn't deployed yet. Flip back to\n  // https://stage-relay-protocol-api.onechain.nexus once it exists.\n  stage: \"https://dev-relay-protocol-api.onechain.nexus\",\n  production: \"https://relay-protocol-api.onechain.nexus\",\n};\n\n/**\n * Resolves the relay API base URL a client should be built against.\n *\n * Resolution order: an explicit `apiBaseUrl` always wins; otherwise falls\n * back to `RELAY_API_BASE_URLS[resolveEnvironment(environment)]` --\n * `resolveEnvironment` normalizes aliases (e.g. \"staging\" -> \"stage\") and,\n * when `environment` itself is omitted, falls back to the global\n * `VITE_CROSSX_ENVIRONMENT` / `NEXT_PUBLIC_CROSSX_ENVIRONMENT` /\n * `CROSSX_ENVIRONMENT` env vars the same way every other dapp-ui feature\n * does (see lib/environment.ts), defaulting to \"production\" when none of\n * those are set either.\n *\n * Always returns a URL -- there is no \"neither provided\" failure case any\n * more, unlike the pre-existing `client`-memo throw branches this feeds.\n * `environment` accepts the same alias-tolerant input `resolveEnvironment`\n * does (wider than the public `Environment`-typed component props), since\n * this is an internal resolution helper, not a prop surface.\n */\nexport function resolveRelayApiBaseUrl(opts: {\n  apiBaseUrl?: string;\n  environment?: Parameters<typeof resolveEnvironment>[0];\n}): string {\n  if (opts.apiBaseUrl) return opts.apiBaseUrl;\n  return RELAY_API_BASE_URLS[resolveEnvironment(opts.environment)];\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { RelayHistoryProps } from \"../types\";\n\n/**\n * Carries `<RelayHistory>`'s own configuration (client/recipient/wallet\n * address/callbacks) from the compound root down to `<RelayHistory.Content>`,\n * which is supplied by the host as a child and so can't receive them as\n * props. Mirrors `RelayDepositContext` (`./relay-deposit-provider.ts`) --\n * kept as its own context rather than shared, since `<RelayDeposit>` and\n * `<RelayHistory>` are independent compounds that don't nest inside one\n * another (each owns its own `ResponsiveShell`/`RelayShellContext`).\n */\nexport const RelayHistoryContext = createContext<RelayHistoryProps | null>(null);\n\nexport function useRelayHistoryContext(): RelayHistoryProps {\n  const ctx = useContext(RelayHistoryContext);\n  if (!ctx) {\n    throw new Error(\n      \"RelayHistory compound components must be used within <RelayHistory>\",\n    );\n  }\n  return ctx;\n}\n","\"use client\";\n\n// The history body: the ONLY component in this feature that calls\n// useRelayOrders for a standalone (non-deposit) view. Task 12's simpler\n// sibling of RelayDepositContent.tsx (Task 11) -- no wizard step, no\n// catalog/target picking, no wallet-injected send/write capabilities. Just:\n// resolve an address (recipient, falling back to walletAddress), build/accept\n// a client (`client` wins, else the effective base URL -- `apiBaseUrl`, or the\n// `environment`-keyed default map when `apiBaseUrl` isn't passed -- builds one\n// via createRelayClient; see resolveRelayApiBaseUrl), and render <OrdersList>\n// against useRelayOrders's result -- or an empty-state notice when there is no\n// valid address to look up at all. The catalog is loaded alongside the list:\n// order amounts are smallest-unit integer strings and cannot be displayed\n// correctly until their token decimals are known.\nimport * as React from \"react\";\n\nimport { TrackingBoundary, trackDappUiFunnel } from \"../../../lib/sentry/index\";\nimport { useRelayOrders } from \"../hooks/useRelayOrders\";\nimport { resolveRelayApiBaseUrl } from \"../lib/api-base\";\nimport { createRelayClient } from \"../lib/client\";\nimport type { Catalog } from \"../lib/types\";\nimport { isAddress } from \"../lib/units\";\nimport { useRelayHistoryContext } from \"../providers/relay-history-provider\";\nimport { useRelayShellContext } from \"../providers/relay-shell-provider\";\nimport { HistoryEmptyState, OrdersList } from \"./OrdersList\";\nimport { ShellContent } from \"./shell/ResponsiveShell\";\nimport \"../styles/component.css\";\n\nexport interface RelayHistoryContentProps {\n  /** Extra class name(s) for the dialog/drawer content element. Falls back to\n   * `<RelayHistory>`'s own `className`. */\n  className?: string;\n}\n\n/**\n * `<RelayHistory.Content>` — the modal/drawer surface. Like\n * `RelayDepositContent`, this is a child of `ShellContent`, i.e. of radix's\n * `Dialog.Portal` / vaul's `Drawer.Portal`, which render nothing while\n * closed: no `listOrders` request happens before the modal is opened, and the\n * subtree unmounts on close -- there is no per-open state here to reset\n * explicitly (unlike `RelayDepositBody`'s step/panel state), so\n * unmount-by-portal is the whole story.\n */\nexport function RelayHistoryContent({ className }: RelayHistoryContentProps) {\n  const props = useRelayHistoryContext();\n  const { open } = useRelayShellContext();\n\n  React.useEffect(() => {\n    if (open) trackDappUiFunnel(\"relay\", \"history_opened\");\n  }, [open]);\n\n  return (\n    <TrackingBoundary feature=\"relay\">\n      <ShellContent ariaTitle=\"Transfer history\" className={className ?? props.className}>\n        <RelayHistoryBody />\n      </ShellContent>\n    </TrackingBoundary>\n  );\n}\n\nfunction RelayHistoryBody() {\n  const props = useRelayHistoryContext();\n  const { theme } = useRelayShellContext();\n\n  // Resolution order: `client` wins; else the effective base URL --\n  // `apiBaseUrl`, or `environment`'s built-in default map when `apiBaseUrl`\n  // isn't passed -- builds one via createRelayClient (see\n  // resolveRelayApiBaseUrl; always yields a URL, so there is no \"neither\n  // provided\" throw case any more). Evaluated unconditionally (not gated on a\n  // resolved recipient) since useRelayOrders always needs a client, even\n  // while it isn't fetching anything yet.\n  const client = React.useMemo(() => {\n    if (props.client) return props.client;\n    const baseUrl = resolveRelayApiBaseUrl({\n      apiBaseUrl: props.apiBaseUrl,\n      environment: props.environment,\n    });\n    return createRelayClient({ baseUrl });\n  }, [props.client, props.apiBaseUrl, props.environment]);\n\n  const resolvedRecipient = props.recipient ?? props.walletAddress;\n  const hasValidRecipient = Boolean(resolvedRecipient && isAddress(resolvedRecipient));\n\n  // History needs the catalog even though it has no token picker: amountIn,\n  // amountOut and expectedOut are API smallest-unit strings. Without token\n  // decimals they render as raw 18-digit integers and force the table to\n  // overflow. Do not request config for the address-less empty state.\n  const [catalog, setCatalog] = React.useState<Catalog>();\n  const [catalogLoading, setCatalogLoading] = React.useState(false);\n  const [catalogError, setCatalogError] = React.useState<Error>();\n  const onErrorRef = React.useRef(props.onError);\n  onErrorRef.current = props.onError;\n\n  React.useEffect(() => {\n    if (!hasValidRecipient) {\n      setCatalog(undefined);\n      setCatalogLoading(false);\n      setCatalogError(undefined);\n      return;\n    }\n\n    let cancelled = false;\n    setCatalog(undefined);\n    setCatalogLoading(true);\n    setCatalogError(undefined);\n\n    client\n      .getConfig()\n      .then((result) => {\n        if (!cancelled) setCatalog(result);\n      })\n      .catch((err) => {\n        if (cancelled) return;\n        const error = err instanceof Error ? err : new Error(String(err));\n        setCatalogError(error);\n        onErrorRef.current?.(error);\n      })\n      .finally(() => {\n        if (!cancelled) setCatalogLoading(false);\n      });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [client, hasValidRecipient]);\n\n  const { orders, ordersLoading, ordersError } = useRelayOrders({\n    client,\n    // Invalid/absent resolved address: pass undefined through, exactly what\n    // useRelayOrders treats as \"clear the list, never fetch.\"\n    recipient: hasValidRecipient ? resolvedRecipient : undefined,\n    // Never paint raw smallest-unit amounts while config is still loading.\n    enabled: Boolean(catalog),\n    onError: props.onError,\n  });\n\n  // Notifies the host of a clicked history-row explorer link without\n  // intercepting navigation (unlike dapp-ui's OnOutlink elsewhere, this is a\n  // fire-and-forget notification -- the anchor's own target=\"_blank\" still\n  // opens the link). Delegated on the wrapper instead of threading a prop\n  // into OrdersList, which renders its own explorer <a> tags.\n  const handleClick = React.useCallback(\n    (e: React.MouseEvent<HTMLDivElement>) => {\n      if (!props.onOutlink) return;\n      const el = e.target as HTMLElement;\n      const anchor = el.closest?.('a[target=\"_blank\"]') as HTMLAnchorElement | null;\n      if (anchor?.href) props.onOutlink(anchor.href);\n    },\n    [props.onOutlink],\n  );\n\n  return (\n    <div className=\"rd-root rd-wizard\" data-theme={theme}>\n      {hasValidRecipient ? (\n        <div onClick={handleClick}>\n          <OrdersList\n            orders={orders}\n            isLoading={catalogLoading || ordersLoading}\n            error={catalogError ?? ordersError}\n            catalog={catalog}\n            // ShellContent keeps an accessible, screen-reader-only title; the\n            // table does not repeat it visually.\n            showTitle={false}\n          />\n        </div>\n      ) : (\n        <HistoryEmptyState requiresIdentity />\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport { ShellTrigger } from \"./shell/ResponsiveShell\";\n\nexport interface RelayHistoryTriggerProps {\n  /** Render the child as the trigger instead of wrapping it. Defaults to\n   * `true` whenever a child is supplied, matching `RelayDepositTrigger`'s\n   * (and `WalletConnectModalTrigger`'s) `asChild ?? children != null` rule. */\n  asChild?: boolean;\n  children?: ReactNode;\n}\n\n/**\n * Thin pass-through to `ShellTrigger` (radix `Dialog.Trigger` on desktop /\n * vaul `Drawer.Trigger` on mobile), mirroring `RelayDepositTrigger` so hosts\n * write `<RelayHistory.Trigger><button>…</button></RelayHistory.Trigger>`\n * without knowing which primitive is active. Falls back to a plain default\n * button when no child is given.\n *\n * Wrapped in `<TrackingBoundary feature=\"relay\">` for the same reason\n * `RelayDepositTrigger`/`WalletConnectModalTrigger` are: `data-track=\"open\"` is\n * only read by the delegate a boundary installs, and this trigger sits OUTSIDE\n * the portaled `<Content>`'s own boundary.\n */\nexport function RelayHistoryTrigger({ asChild, children }: RelayHistoryTriggerProps) {\n  return (\n    <TrackingBoundary feature=\"relay\">\n      <ShellTrigger asChild={asChild ?? children != null}>\n        {children ?? (\n          <button type=\"button\" data-track=\"open\">\n            History\n          </button>\n        )}\n      </ShellTrigger>\n    </TrackingBoundary>\n  );\n}\n","\"use client\";\n\n// The compound root: `<RelayHistory>` + `.Trigger` + `.Content` — the\n// standalone transfer-history modal. Task 12's simpler sibling of\n// `RelayDeposit.tsx` (Task 11): same wiring shape (every prop goes into a\n// context so `.Content`, supplied by the host as a child, can read them; the\n// shell knobs go into `ResponsiveShell`), but with no wizard state of its\n// own -- `<RelayHistory.Content>` is just `useRelayOrders` + `<OrdersList>`.\nimport { RelayHistoryContext } from \"../providers/relay-history-provider\";\nimport type { RelayHistoryProps } from \"../types\";\nimport { RelayHistoryContent } from \"./RelayHistoryContent\";\nimport { RelayHistoryTrigger } from \"./RelayHistoryTrigger\";\nimport { ResponsiveShell } from \"./shell/ResponsiveShell\";\nimport \"../styles/component.css\";\n\nfunction RelayHistoryRoot(props: RelayHistoryProps) {\n  const {\n    theme = \"dark\",\n    mobileBreakpoint,\n    drawerDirection,\n    // The history table needs more horizontal room than the deposit form.\n    // Keep the shell overrideable, but make the standalone history modal wide\n    // enough to show all six desktop columns without horizontal scrolling.\n    dialogWidth = \"min(640px, calc(100vw - 32px))\",\n    drawerMaxWidth,\n    drawerMinWidth,\n    style,\n    open,\n    onOpenChange,\n    children,\n  } = props;\n\n  return (\n    // `props` is a fresh object each render, so consumers re-render exactly when\n    // this root does -- same rationale as RelayDepositRoot.\n    <RelayHistoryContext.Provider value={props}>\n      <ResponsiveShell\n        theme={theme}\n        mobileBreakpoint={mobileBreakpoint}\n        drawerDirection={drawerDirection}\n        dialogWidth={dialogWidth}\n        drawerMaxWidth={drawerMaxWidth}\n        drawerMinWidth={drawerMinWidth}\n        style={style}\n        open={open}\n        onOpenChange={onOpenChange}\n      >\n        {children}\n      </ResponsiveShell>\n    </RelayHistoryContext.Provider>\n  );\n}\n\nexport const RelayHistory = Object.assign(RelayHistoryRoot, {\n  Trigger: RelayHistoryTrigger,\n  Content: RelayHistoryContent,\n});\n","\"use client\";\n\n// The compound root: `<RelayRecovery>` + `.Trigger` + `.Content` — the\n// standalone \"Recover stuck funds\" modal, i.e. the deposit wizard's gear panel\n// as a modal of its own (the gear inside `<RelayDeposit>` is untouched and\n// still opt-in via `showRecovery`).\n//\n// Structurally `RelayHistory.tsx`: every prop goes into a context so `.Content`,\n// supplied by the host as a child, can read them; the shell knobs go into\n// `ResponsiveShell`; and there is no wizard state to lift above the shell\n// (unlike `RelayDeposit`, which needs `RelayWizardStateHost` for that).\n//\n// It does own two things beyond the history modal:\n//\n//   * the modal's OPEN flag, resolved here and driving `ResponsiveShell` fully\n//     controlled (verbatim from `RelayDepositRoot`, and for a related reason:\n//     something above the shell has to know when the modal closes);\n//   * the shared recovery snapshot (`useRecoverableCount`). It has to be fetched\n//     HERE because the trigger needs its count while closed. The modal reuses the\n//     same per-target data instead of repeating the fan-out on every open.\nimport * as React from \"react\";\n\nimport { useRecoverableCount } from \"../hooks/useRecoverableCount\";\nimport { resolveRelayApiBaseUrl } from \"../lib/api-base\";\nimport { createRelayClient } from \"../lib/client\";\nimport { RelayRecoveryContext } from \"../providers/relay-recovery-provider\";\nimport type { RelayRecoveryProps } from \"../types\";\nimport { isAddress } from \"../lib/units\";\nimport { RelayRecoveryContent } from \"./RelayRecoveryContent\";\nimport { RelayRecoveryTrigger } from \"./RelayRecoveryTrigger\";\nimport { ResponsiveShell } from \"./shell/ResponsiveShell\";\nimport \"../styles/component.css\";\n\nfunction RelayRecoveryRoot(props: RelayRecoveryProps) {\n  const {\n    theme = \"dark\",\n    mobileBreakpoint,\n    drawerDirection,\n    dialogWidth,\n    drawerMaxWidth,\n    drawerMinWidth,\n    style,\n    open: openProp,\n    onOpenChange,\n    children,\n  } = props;\n\n  // Same controlled/uncontrolled contract `ResponsiveShell` implements, resolved\n  // one level higher (verbatim from `RelayDepositRoot`): an `open` prop wins\n  // outright, `onOpenChange` is notified either way, and the shell below is\n  // always driven fully controlled.\n  const [internalOpen, setInternalOpen] = React.useState(false);\n  const isControlled = openProp !== undefined;\n  const open = isControlled ? openProp : internalOpen;\n\n  const handleOpenChange = React.useCallback(\n    (value: boolean) => {\n      if (!isControlled) setInternalOpen(value);\n      onOpenChange?.(value);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  // Same resolution order the content uses (`client` > `apiBaseUrl` >\n  // `environment`), so the badge and the panel always talk to one backend.\n  const client = React.useMemo(() => {\n    if (props.client) return props.client;\n    const baseUrl = resolveRelayApiBaseUrl({\n      apiBaseUrl: props.apiBaseUrl,\n      environment: props.environment,\n    });\n    return createRelayClient({ baseUrl });\n  }, [props.client, props.apiBaseUrl, props.environment]);\n\n  const connectedWallet =\n    props.walletAddress && isAddress(props.walletAddress)\n      ? props.walletAddress\n      : undefined;\n  const recoveryRecipient =\n    connectedWallet &&\n    (!props.recipient ||\n      (isAddress(props.recipient) &&\n        props.recipient.toLowerCase() === connectedWallet.toLowerCase()))\n      ? connectedWallet\n      : undefined;\n\n  const recovery = useRecoverableCount({\n    client,\n    // Recovery is signing-first. Never prefetch another address merely because\n    // the public API accepts `?user=`; the UI is pinned to the connected wallet.\n    recipient: recoveryRecipient,\n  });\n\n  // A later open may need fresher balances, but rapid close/reopen cycles reuse\n  // the recent snapshot. The hook also coalesces an open that happens while the\n  // initial closed-trigger request is still in flight.\n  React.useEffect(() => {\n    if (open) recovery.refreshIfStale();\n  }, [open, recovery.refreshIfStale]);\n\n  // Report each failed snapshot once from the persistent root. The modal body\n  // unmounts on close, so reporting there repeated the same cached error on\n  // every reopen (and whenever an inline callback changed identity).\n  const reportedError = React.useRef<Error | undefined>(undefined);\n  React.useEffect(() => {\n    if (!open || !recovery.error || reportedError.current === recovery.error) return;\n    reportedError.current = recovery.error;\n    props.onError?.(recovery.error);\n  }, [open, recovery.error, props.onError]);\n\n  // `props` is a fresh object each render, so consumers re-render exactly when\n  // this root does -- same rationale as RelayHistoryRoot (memoizing it would\n  // only pretend to be stable; every field is a prop). Note that the recovery\n  // body does NOT key any effect on this object's identity: it re-bundles the\n  // injected wallet functions through its own memo first (see\n  // RelayRecoveryContent's `wallet`).\n  const contextValue = React.useMemo(\n    () => ({ props, recovery }),\n    [props, recovery],\n  );\n\n  return (\n    <RelayRecoveryContext.Provider value={contextValue}>\n      <ResponsiveShell\n        theme={theme}\n        mobileBreakpoint={mobileBreakpoint}\n        drawerDirection={drawerDirection}\n        dialogWidth={dialogWidth}\n        drawerMaxWidth={drawerMaxWidth}\n        drawerMinWidth={drawerMinWidth}\n        style={style}\n        open={open}\n        onOpenChange={handleOpenChange}\n      >\n        {children}\n      </ResponsiveShell>\n    </RelayRecoveryContext.Provider>\n  );\n}\n\nexport const RelayRecovery = Object.assign(RelayRecoveryRoot, {\n  Trigger: RelayRecoveryTrigger,\n  Content: RelayRecoveryContent,\n});\n","\"use client\";\n\n// Shared recovery snapshot for `<RelayRecovery>`'s trigger and modal body.\n//\n// Extracted from the equivalent fan-out inside `RelayWizardStateHost`'s engine\n// (which badges the deposit wizard's gear): read the catalog once, then\n// GET /v1/recovery per target, and sum the tokens the server flags\n// `recoverable`. Kept as its own hook rather than shared with the wizard because\n// the two differ in three ways the wizard's version can't express:\n//\n//   * it runs at the COMPOUND ROOT, not inside the modal -- a badge whose count\n//     only appeared once you opened the modal would be pointless, so this fetch\n//     deliberately fires for a mounted CLOSED modal;\n//   * a catalog with NO targets still gets one lookup (`target: undefined`, the\n//     configured default) instead of a flat zero, because that is exactly what\n//     `<RelayRecovery.Content>` goes on to inspect in that case;\n//   * it needs no wallet capability at all. The count is pure HTTP; whether the\n//     connected wallet can actually SIGN a recovery is the modal body's concern\n//     (see RelayRecoveryContent's trio gate), not the badge's.\n//\n// Gated on a resolved, valid recipient and nothing else: a disconnected page\n// makes zero requests. No polling either -- a confirmed recovery explicitly\n// calls `refresh()`. Rapid opening and closing reuses this snapshot; an open\n// refreshes only after the previous attempt is 30 seconds old.\nimport * as React from \"react\";\n\nimport type { RelayClient } from \"../lib/client\";\nimport { countRecoverableTokens, groupForwarders, type RecoveryTargetEntry } from \"../lib/recoveryGroups\";\nimport { isAddress } from \"../lib/units\";\n\nexport interface UseRecoverableCountOptions {\n  /** Already-resolved client (see resolveRelayApiBaseUrl / createRelayClient). */\n  client: RelayClient;\n  /** The address to count for. Absent/invalid -- zero, and zero requests. */\n  recipient?: string;\n}\n\nexport interface UseRecoverableCountResult {\n  /** Recoverable tokens summed by forwarder; `0` also covers loading/failure. */\n  count: number;\n  /** The same per-target data consumed by the modal body. */\n  targets: readonly RecoveryTargetEntry[];\n  /** True only until this recipient's first snapshot settles. */\n  loading: boolean;\n  /** Snapshot failure, retained so an open modal can report it to the host. */\n  error?: Error;\n  /** Re-fetch after a recovery transaction changes on-chain state. */\n  refresh: () => void;\n  /** Re-fetch on open only when the last attempt is old enough. */\n  refreshIfStale: () => void;\n}\n\ninterface RecoverySnapshotState {\n  recipientKey?: string;\n  targets: readonly RecoveryTargetEntry[];\n  badgeFailed?: boolean;\n  error?: Error;\n}\n\ninterface RecoverySnapshotResponse {\n  targets: readonly RecoveryTargetEntry[];\n  badgeFailed: boolean;\n  error?: Error;\n}\n\nconst EMPTY_TARGETS: readonly RecoveryTargetEntry[] = [];\nconst RECOVERY_SNAPSHOT_STALE_MS = 30_000;\n\n/**\n * Fetches the recovery data once at the compound root. The trigger reads the\n * aggregate count and the modal body reads `targets`, avoiding a second fan-out\n * on every open. A broken/unreachable backend still degrades to a zero badge.\n */\nexport function useRecoverableCount({\n  client,\n  recipient,\n}: UseRecoverableCountOptions): UseRecoverableCountResult {\n  const [snapshot, setSnapshot] = React.useState<RecoverySnapshotState>({\n    targets: EMPTY_TARGETS,\n  });\n  const [refreshNonce, setRefreshNonce] = React.useState(0);\n  const inFlightRef = React.useRef<\n    | {\n        client: RelayClient;\n        recipientKey: string;\n        generation: number;\n        promise: Promise<RecoverySnapshotResponse>;\n      }\n    | undefined\n  >(undefined);\n  const lastAttemptRef = React.useRef<\n    | {\n        client: RelayClient;\n        recipientKey: string;\n        at: number;\n      }\n    | undefined\n  >(undefined);\n\n  const recipientKey = recipient && isAddress(recipient) ? recipient.toLowerCase() : undefined;\n\n  React.useEffect(() => {\n    if (!recipientKey) {\n      setSnapshot({ targets: EMPTY_TARGETS });\n      return;\n    }\n\n    let cancelled = false;\n    let pending = inFlightRef.current;\n    if (\n      !pending ||\n      pending.client !== client ||\n      pending.recipientKey !== recipientKey ||\n      pending.generation !== refreshNonce\n    ) {\n      const promise = (async (): Promise<RecoverySnapshotResponse> => {\n        let catalogError: Error | undefined;\n        let targets = [] as Awaited<ReturnType<RelayClient[\"getConfig\"]>>[\"targets\"];\n        try {\n          targets = (await client.getConfig()).targets;\n        } catch (err) {\n          // Preserve the old modal fallback: if the catalog is unavailable,\n          // inspect the backend's configured default target once.\n          catalogError = err instanceof Error ? err : new Error(String(err));\n        }\n\n        // No targets (or an older/unavailable catalog): one lookup against the\n        // configured default, mirroring the pre-multi-target panel behavior.\n        const lookups: Array<() => ReturnType<RelayClient[\"getRecovery\"]>> =\n          targets.length === 0\n            ? [() => client.getRecovery({ user: recipientKey })]\n            : targets.map(\n                (t) => () => client.getRecovery({ user: recipientKey, target: t.symbol }),\n              );\n        // Per-target failures do not erase successful targets, but are retained\n        // for the open modal's error callback instead of disappearing silently.\n        const results = await Promise.all(\n          lookups.map(async (lookup) => {\n            try {\n              return { info: await lookup() };\n            } catch (err) {\n              return { error: err instanceof Error ? err : new Error(String(err)) };\n            }\n          }),\n        );\n        const entries: RecoveryTargetEntry[] =\n          targets.length === 0\n            ? results[0]?.info\n              ? [{ symbol: \"\", isDefault: true, info: results[0].info }]\n              : []\n            : targets.flatMap((t, i) =>\n                results[i]?.info\n                  ? [{ symbol: t.symbol, isDefault: t.isDefault, info: results[i].info }]\n                  : [],\n              );\n        const recoveryError = results.find((result) => result.error)?.error;\n        return {\n          targets: entries,\n          badgeFailed: Boolean(catalogError || (recoveryError && entries.length === 0)),\n          error: catalogError ?? recoveryError,\n        };\n      })().catch((err): RecoverySnapshotResponse => ({\n        targets: EMPTY_TARGETS,\n        badgeFailed: true,\n        error: err instanceof Error ? err : new Error(String(err)),\n      }));\n      pending = { client, recipientKey, generation: refreshNonce, promise };\n      inFlightRef.current = pending;\n      void promise.finally(() => {\n        lastAttemptRef.current = {\n          client,\n          recipientKey,\n          at: Date.now(),\n        };\n        if (inFlightRef.current?.promise === promise) inFlightRef.current = undefined;\n      });\n    }\n\n    void pending.promise.then((result) => {\n      if (cancelled) return;\n      setSnapshot({\n        recipientKey,\n        targets: result.targets,\n        badgeFailed: result.badgeFailed,\n        error: result.error,\n      });\n    });\n\n    return () => {\n      cancelled = true;\n    };\n  }, [client, recipientKey, refreshNonce]);\n\n  const refresh = React.useCallback(() => setRefreshNonce((value) => value + 1), []);\n  const refreshIfStale = React.useCallback(() => {\n    if (!recipientKey) return;\n    const pending = inFlightRef.current;\n    if (\n      pending?.client === client &&\n      pending.recipientKey === recipientKey\n    ) {\n      return;\n    }\n    const lastAttempt = lastAttemptRef.current;\n    if (\n      lastAttempt?.client === client &&\n      lastAttempt.recipientKey === recipientKey &&\n      Date.now() - lastAttempt.at < RECOVERY_SNAPSHOT_STALE_MS\n    ) {\n      return;\n    }\n    setRefreshNonce((value) => value + 1);\n  }, [client, recipientKey]);\n  const targets =\n    recipientKey && snapshot.recipientKey === recipientKey\n      ? snapshot.targets\n      : EMPTY_TARGETS;\n  const error =\n    recipientKey && snapshot.recipientKey === recipientKey\n      ? snapshot.error\n      : undefined;\n  const loading = Boolean(\n    recipientKey && snapshot.recipientKey !== recipientKey,\n  );\n  const badgeFailed = Boolean(\n    recipientKey && snapshot.recipientKey === recipientKey && snapshot.badgeFailed,\n  );\n\n  return React.useMemo(\n    () => ({\n      // A catalog failure still gives the open modal one default-target\n      // fallback, but the closed trigger keeps its established fail-soft `0`.\n      count: badgeFailed ? 0 : countRecoverableTokens(groupForwarders(targets)),\n      targets,\n      loading,\n      error,\n      refresh,\n      refreshIfStale,\n    }),\n    [targets, loading, badgeFailed, error, refresh, refreshIfStale],\n  );\n}\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { UseRecoverableCountResult } from \"../hooks/useRecoverableCount\";\nimport type { RelayRecoveryProps } from \"../types\";\n\n/**\n * What `<RelayRecovery>` publishes to its compound children.\n *\n * `props` is the root's own configuration (client/recipient/injected wallet\n * capabilities/callbacks), which `<RelayRecovery.Content>` needs but can't\n * receive as props (the host supplies it as a child).\n *\n * `recovery` is the root-level snapshot shared by `.Trigger` and `.Content`.\n * It lives here because the trigger needs the count while the modal is CLOSED,\n * and sharing its target data prevents the content from repeating the same\n * config/recovery fan-out on every open.\n *\n * Mirrors `RelayHistoryContext` (`./relay-history-provider.ts`) -- kept as its\n * own context rather than shared, since `<RelayDeposit>`, `<RelayHistory>` and\n * `<RelayRecovery>` are independent compounds that don't nest inside one another\n * (each owns its own `ResponsiveShell`/`RelayShellContext`).\n */\nexport interface RelayRecoveryContextValue {\n  props: RelayRecoveryProps;\n  recovery: UseRecoverableCountResult;\n}\n\nexport const RelayRecoveryContext = createContext<RelayRecoveryContextValue | null>(null);\n\nfunction useRelayRecoveryContextValue(): RelayRecoveryContextValue {\n  const ctx = useContext(RelayRecoveryContext);\n  if (!ctx) {\n    throw new Error(\n      \"RelayRecovery compound components must be used within <RelayRecovery>\",\n    );\n  }\n  return ctx;\n}\n\nexport function useRelayRecoveryContext(): RelayRecoveryProps {\n  return useRelayRecoveryContextValue().props;\n}\n\n/** Root snapshot consumed by the recovery modal body. */\nexport function useRelayRecoverySnapshot(): UseRecoverableCountResult {\n  return useRelayRecoveryContextValue().recovery;\n}\n\n/** `.Trigger`'s badge count. See `RelayRecoveryContextValue.recovery`. */\nexport function useRelayRecoveryCount(): number {\n  return useRelayRecoveryContextValue().recovery.count;\n}\n","\"use client\";\n\n// The recovery body: `<RecoverFunds>` (the very same panel the deposit wizard's\n// gear opens) hosted directly in the shared modal shell. The visible title/X\n// are intentionally absent; ShellContent keeps the accessible sr-only title\n// and the shell owns Escape/outside-click/swipe dismissal. The old\n// delivery-token picker is gone (upstream e5bff75): the panel now runs in\n// per-forwarder mode and lists EVERY forwarder holding funds at once, so there\n// is no \"which target to inspect\" left to pick.\n//\n// Sibling of RelayHistoryContent.tsx: same wiring shape (read every prop off\n// the compound context, resolve `client` from `client` > `apiBaseUrl` >\n// `environment` via resolveRelayApiBaseUrl, wrap in TrackingBoundary +\n// ShellContent), and the same \"no request before the modal opens\" property for\n// free -- this subtree lives inside radix `Dialog.Portal` / vaul\n// `Drawer.Portal`, which render nothing while closed, and unmounts on close.\n//\n// Two differences from the history modal, both intrinsic to recovery:\n//\n//   * it needs INJECTED WALLET CAPABILITIES. Recovery is signing-first (the\n//     user pays BSC gas for a permissionless forwarder deploy/sweep), so a\n//     wallet missing the writeContract/readContract/waitForReceipt trio that\n//     useRecovery.recover() guards on gets a one-line note instead of a panel\n//     whose only button could never work;\n//   * it consumes the root's shared config/recovery snapshot. The trigger needs\n//     that snapshot while closed, so fetching it again here would multiply API\n//     calls on every close/reopen cycle.\nimport * as React from \"react\";\n\nimport { TrackingBoundary, trackDappUiFunnel } from \"../../../lib/sentry/index\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport {\n  useRemoteConfigValue,\n  type AppsConfig,\n  resolveRelayForwarderFactories,\n} from \"../../../lib/remote-config\";\nimport { useRelayOrders } from \"../hooks/useRelayOrders\";\nimport { resolveRelayApiBaseUrl } from \"../lib/api-base\";\nimport { mergeTrustedFactories } from \"../lib/verifyForwarder\";\nimport { createRelayClient } from \"../lib/client\";\nimport { isAddress } from \"../lib/units\";\nimport {\n  useRelayRecoveryContext,\n  useRelayRecoverySnapshot,\n} from \"../providers/relay-recovery-provider\";\nimport { useRelayShellContext } from \"../providers/relay-shell-provider\";\nimport { RecoverFunds } from \"./RecoverFunds\";\nimport { ShellContent } from \"./shell/ResponsiveShell\";\nimport type { RelayWalletProps } from \"../types\";\nimport \"../styles/component.css\";\n\nexport interface RelayRecoveryContentProps {\n  /** Extra class name(s) for the dialog/drawer content element. Falls back to\n   * `<RelayRecovery>`'s own `className`. */\n  className?: string;\n}\n\n/**\n * `<RelayRecovery.Content>` — the modal/drawer surface. Same portal-scoped\n * lifecycle as `<RelayHistory.Content>`: nothing is requested before the modal\n * is opened, and the whole subtree (including `useRecovery`'s state and any\n * in-flight recovery tracking) unmounts on close, so there is no per-open state\n * to reset explicitly.\n */\nexport function RelayRecoveryContent({ className }: RelayRecoveryContentProps) {\n  const props = useRelayRecoveryContext();\n  const { open } = useRelayShellContext();\n\n  React.useEffect(() => {\n    if (open) trackDappUiFunnel(\"relay\", \"recovery_opened\");\n  }, [open]);\n\n  return (\n    <TrackingBoundary feature=\"relay\">\n      <ShellContent ariaTitle=\"Recover stuck funds\" className={className ?? props.className}>\n        <RelayRecoveryBody />\n      </ShellContent>\n    </TrackingBoundary>\n  );\n}\n\nfunction RelayRecoveryBody() {\n  const props = useRelayRecoveryContext();\n  const {\n    targets: recoveryTargets,\n    loading: recoveryLoading,\n    refresh: refreshRecovery,\n  } = useRelayRecoverySnapshot();\n  const { theme } = useRelayShellContext();\n\n  // Resolution order: `client` (already built, needs no URL at all) >\n  // `apiBaseUrl` > `environment`'s built-in default map. Resolved once and\n  // threaded into BOTH the catalog client below and <RecoverFunds> (whose own\n  // useRecovery builds a third client from it), so all three agree.\n  const resolvedApiBaseUrl = React.useMemo(\n    () => resolveRelayApiBaseUrl({ apiBaseUrl: props.apiBaseUrl, environment: props.environment }),\n    [props.apiBaseUrl, props.environment],\n  );\n\n  const client = React.useMemo(() => {\n    if (props.client) return props.client;\n    return createRelayClient({ baseUrl: resolvedApiBaseUrl });\n  }, [props.client, resolvedApiBaseUrl]);\n\n  // The injected capabilities, re-bundled into the shape <RecoverFunds> ->\n  // useRecovery takes. Memoized on the individual functions (same rationale as\n  // RelayWizardStateHost's own `wallet` memo) so a host passing stable\n  // references doesn't hand useRecovery a fresh object every render.\n  const wallet = React.useMemo<RelayWalletProps>(\n    () => ({\n      walletAddress: props.walletAddress,\n      walletChainId: props.walletChainId,\n      sendTransaction: props.sendTransaction,\n      writeContract: props.writeContract,\n      readContract: props.readContract,\n      switchChain: props.switchChain,\n      waitForReceipt: props.waitForReceipt,\n      getBalance: props.getBalance,\n    }),\n    [\n      props.walletAddress,\n      props.walletChainId,\n      props.sendTransaction,\n      props.writeContract,\n      props.readContract,\n      props.switchChain,\n      props.waitForReceipt,\n      props.getBalance,\n    ],\n  );\n\n  const connectedWallet =\n    props.walletAddress && isAddress(props.walletAddress)\n      ? props.walletAddress\n      : undefined;\n  const recipientMatchesWallet = Boolean(\n    connectedWallet &&\n      (!props.recipient ||\n        (isAddress(props.recipient) &&\n          props.recipient.toLowerCase() === connectedWallet.toLowerCase())),\n  );\n  const resolvedRecipient = recipientMatchesWallet ? connectedWallet : undefined;\n\n  // Same CDN resolution the deposit wizard applies (see RelayWizardStateHost):\n  // Only role-labelled `forwarderFactories` is read by current code. The\n  // legacy `trustedFactories` compatibility array is deliberately ignored.\n  // The resolved list REPLACES the pinned defaults; an explicit host prop wins.\n  const resolvedEnv = resolveEnvironment(props.environment);\n  const remoteTrusted = useRemoteConfigValue<AppsConfig, string[] | undefined>(\n    \"apps.json\",\n    resolvedEnv,\n    (config) => resolveRelayForwarderFactories(config, resolvedEnv),\n  );\n  const effectiveTrustedFactories = React.useMemo(\n    () => props.trustedFactories ?? mergeTrustedFactories(remoteTrusted),\n    [props.trustedFactories, remoteTrusted],\n  );\n\n  // The exact trio useRecovery.recover() guards on: missing any one of them\n  // means every button in the panel would throw on click, so the panel is not\n  // rendered at all. `getBalance`/`sendTransaction` are irrelevant here --\n  // recovery never reads a balance or does a plain transfer.\n  const canSign = Boolean(props.writeContract && props.readContract && props.waitForReceipt);\n  const ready = Boolean(resolvedRecipient) && canSign;\n  const recipientKey = resolvedRecipient;\n\n  // Read-only provenance for the balance-composition rows -- which deposits\n  // make up each forwarder's balance (BSC_FUNDED orders only; see\n  // ../lib/recoveryGroups.ts).\n  const { orders } = useRelayOrders({\n    client,\n    recipient: ready ? recipientKey : undefined,\n    enabled: ready,\n    // Orders only annotate the current recovery balance. One snapshot is\n    // enough; opening an SSE connection plus its on-open refetch here doubled\n    // API traffic on every modal reopen.\n    live: false,\n  });\n\n  return (\n    <div className=\"rd-root rd-wizard\" data-theme={theme}>\n      {!connectedWallet && (\n        <p className=\"rd-muted\" data-testid=\"rd-recovery-connect-note\">\n          Connect a wallet to check recoverable funds.\n        </p>\n      )}\n\n      {connectedWallet && !recipientMatchesWallet && (\n        <p className=\"rd-muted\" data-testid=\"rd-recovery-recipient-note\">\n          Recovery is only available for the connected wallet.\n        </p>\n      )}\n\n      {recipientMatchesWallet && !canSign && (\n        <p className=\"rd-muted\" data-testid=\"rd-recovery-unsupported-note\">\n          This wallet can&apos;t sign recovery transactions.\n        </p>\n      )}\n\n      {ready && (\n        <RecoverFunds\n          client={props.client}\n          apiBaseUrl={resolvedApiBaseUrl}\n          recipient={resolvedRecipient}\n          wallet={wallet}\n          trustedFactories={effectiveTrustedFactories}\n          // Always supply the root snapshot, including its empty/loading state,\n          // so RecoverFunds never starts a duplicate single-target request.\n          targets={recoveryTargets}\n          targetsLoading={recoveryLoading}\n          orders={orders}\n          onRecovered={refreshRecovery}\n          onRecoverySuccess={props.onRecoverySuccess}\n          // ShellContent owns the screen-reader-only modal title.\n          showTitle={false}\n          onError={props.onError}\n        />\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport { useRelayRecoveryCount } from \"../providers/relay-recovery-provider\";\nimport { ShellTrigger } from \"./shell/ResponsiveShell\";\nimport \"../styles/component.css\";\n\nexport interface RelayRecoveryTriggerProps {\n  /** Render the child as the trigger instead of wrapping it. Defaults to\n   * `true` whenever a child is supplied, matching `RelayHistoryTrigger`'s\n   * (and `RelayDepositTrigger`'s) `asChild ?? children != null` rule. */\n  asChild?: boolean;\n  children?: ReactNode;\n}\n\n/**\n * Thin pass-through to `ShellTrigger` (radix `Dialog.Trigger` on desktop /\n * vaul `Drawer.Trigger` on mobile), mirroring `RelayHistoryTrigger` so hosts\n * write `<RelayRecovery.Trigger><button>…</button></RelayRecovery.Trigger>`\n * without knowing which primitive is active. Falls back to a plain-text\n * \"Recover\" button when no child is given -- unstyled like the sibling\n * `RelayDepositTrigger`/`RelayHistoryTrigger` defaults, so it inherits the\n * host's own button chrome instead of shipping a look of its own.\n *\n * The default button renders NO count badge (2026-07-30: removed on request --\n * the number read as an unread-notification alarm). The root's cross-target\n * recoverable count still reaches the trigger element as\n * `data-rd-recovery-count` (radix/vaul merge Trigger props onto the `asChild`\n * child too), so a host that WANTS a badge can opt in with its own CSS -- e.g.\n * `button[data-rd-recovery-count]:not([data-rd-recovery-count=\"0\"])::after` --\n * without refetching anything.\n *\n * Wrapped in `<TrackingBoundary feature=\"relay\">` for the same reason\n * `RelayHistoryTrigger` is: `data-track=\"open\"` is only read by the delegate a\n * boundary installs, and this trigger sits OUTSIDE the portaled `<Content>`'s\n * own boundary.\n */\nexport function RelayRecoveryTrigger({ asChild, children }: RelayRecoveryTriggerProps) {\n  const recoverableCount = useRelayRecoveryCount();\n\n  return (\n    <TrackingBoundary feature=\"relay\">\n      <ShellTrigger\n        asChild={asChild ?? children != null}\n        dataAttributes={{ \"data-rd-recovery-count\": recoverableCount }}\n      >\n        {children ?? (\n          <button type=\"button\" data-track=\"open\">\n            Recover\n          </button>\n        )}\n      </ShellTrigger>\n    </TrackingBoundary>\n  );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { createRelayClient, type RelayClient } from \"../lib/client\";\nimport type { Catalog } from \"../lib/types\";\n\nexport interface UseRelayConfigOptions {\n  /** Pre-built client. Takes precedence over apiBaseUrl. */\n  client?: RelayClient;\n  /** Used to build a client when `client` is not supplied. */\n  apiBaseUrl?: string;\n  /** Stops the request and clears the current snapshot. Defaults to true. */\n  enabled?: boolean;\n  /** Optional automatic REST refresh cadence in milliseconds. Disabled when omitted or <= 0. */\n  refetchInterval?: number;\n  onError?: (error: Error) => void;\n}\n\nexport interface UseRelayConfigResult {\n  /** Latest GET /v1/config response. */\n  config?: Catalog;\n  loading: boolean;\n  error?: Error;\n  /** Re-fetches GET /v1/config using the current client. */\n  refresh: () => void;\n}\n\n/**\n * Public read-only hook for Relay's origin/destination/target catalog.\n *\n * The response is a normal REST snapshot, not an SSE stream. Use\n * `refetchInterval` for periodic refreshes or call `refresh` for an immediate\n * read without replacing the client or remounting the component.\n */\nexport function useRelayConfig(opts: UseRelayConfigOptions = {}): UseRelayConfigResult {\n  const optsRef = useRef(opts);\n  optsRef.current = opts;\n\n  const client = useMemo(() => {\n    if (opts.client) return opts.client;\n    if (!opts.apiBaseUrl) {\n      throw new Error(\"useRelayConfig: either `client` or `apiBaseUrl` must be provided\");\n    }\n    return createRelayClient({ baseUrl: opts.apiBaseUrl });\n  }, [opts.client, opts.apiBaseUrl]);\n\n  const enabled = opts.enabled ?? true;\n  const refetchInterval = opts.refetchInterval;\n  const [config, setConfig] = useState<Catalog>();\n  const [loading, setLoading] = useState(false);\n  const [error, setError] = useState<Error>();\n  const [refreshNonce, setRefreshNonce] = useState(0);\n\n  useEffect(() => {\n    if (!enabled) {\n      setConfig(undefined);\n      setLoading(false);\n      setError(undefined);\n      return;\n    }\n\n    let cancelled = false;\n    let inFlight = false;\n    setConfig(undefined);\n    setLoading(true);\n    setError(undefined);\n\n    async function refetchConfig() {\n      if (cancelled || inFlight) return;\n      inFlight = true;\n      try {\n        const result = await client.getConfig();\n        if (cancelled) return;\n        setConfig(result);\n        setError(undefined);\n      } catch (err) {\n        if (cancelled) return;\n        const nextError = err instanceof Error ? err : new Error(String(err));\n        setError(nextError);\n        optsRef.current.onError?.(nextError);\n      } finally {\n        inFlight = false;\n        if (!cancelled) setLoading(false);\n      }\n    }\n\n    void refetchConfig();\n    const refetchTimer =\n      typeof refetchInterval === \"number\" &&\n      Number.isFinite(refetchInterval) &&\n      refetchInterval > 0\n        ? setInterval(() => {\n            void refetchConfig();\n          }, refetchInterval)\n        : undefined;\n\n    return () => {\n      cancelled = true;\n      if (refetchTimer) clearInterval(refetchTimer);\n    };\n  }, [client, enabled, refreshNonce, refetchInterval]);\n\n  const refresh = useCallback(() => setRefreshNonce((value) => value + 1), []);\n\n  return { config, loading, error, refresh };\n}\n","\"use client\";\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport type { UseQueryOptions } from \"@tanstack/react-query\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\n\nimport { createRelayClient, RelayApiError } from \"../lib/client\";\nimport type { RelayClient } from \"../lib/client\";\nimport type { RecoveryInfo } from \"../lib/types\";\nimport { isAddress } from \"../lib/units\";\nimport type { RecoverySuccessResult, RelayWalletProps } from \"../types\";\nimport { useRecoveryActions } from \"./useRecovery\";\nimport type { RecoveryAction, RecoveryState } from \"./useRecovery\";\n\nlet nextClientId = 1;\nconst clientIds = new WeakMap<object, number>();\n\nfunction getClientId(client: RelayClient): number {\n  const existing = clientIds.get(client);\n  if (existing !== undefined) return existing;\n  const id = nextClientId++;\n  clientIds.set(client, id);\n  return id;\n}\n\nexport type RelayRecoveryQueryKey = readonly [\n  \"relay\",\n  \"recovery\",\n  number,\n  string | null,\n  string | null,\n];\n\n/**\n * React Query policies accepted by useRelayRecovery. The SDK owns the request\n * identity and response shape, so callers cannot replace queryKey/queryFn or\n * inject/select recovery balances.\n */\nexport type RelayRecoveryQueryOptions = Omit<\n  UseQueryOptions<RecoveryInfo, Error, RecoveryInfo, RelayRecoveryQueryKey>,\n  | \"queryKey\"\n  | \"queryFn\"\n  | \"select\"\n  | \"initialData\"\n  | \"initialDataUpdatedAt\"\n  | \"placeholderData\"\n>;\n\nexport interface UseRelayRecoveryOptions {\n  /** Pre-built client. Takes precedence over apiBaseUrl. */\n  client?: RelayClient;\n  /** Used to build a client via createRelayClient when client isn't passed. */\n  apiBaseUrl?: string;\n  /** Connected EVM wallet whose recoverable forwarder balances are queried. */\n  recipient?: string;\n  /** Optional CROSS delivery target passed to GET /v1/recovery. */\n  target?: string;\n  /** React Query lifecycle, cache, retry, and refetch policies. */\n  query?: RelayRecoveryQueryOptions;\n  /** Optional wallet capabilities. Read-only consumers may omit this. */\n  wallet?: RelayWalletProps;\n  /** Called when GET /v1/recovery returns 401. */\n  onUnauthorized?: () => void;\n  /** Called after a terminal query or recovery-action error. */\n  onError?: (error: Error) => void;\n  /** Called once after a recovery transaction confirms successfully. */\n  onRecoverySuccess?: (result: RecoverySuccessResult) => void;\n  /** Trusted factory override for a self-hosted deployment. */\n  trustedFactories?: readonly string[];\n}\n\nexport interface UseRelayRecoveryResult {\n  info?: RecoveryInfo;\n  loading: boolean;\n  error?: Error;\n  /** True for background refetches as well as the first request. */\n  isFetching: boolean;\n  /** Invalidates no identities; immediately refetches this hook's fixed query. */\n  refresh: () => void;\n  /** Signs a recovery transaction for the currently fetched info. */\n  recover: (action: RecoveryAction, tokenAddress?: string) => Promise<void>;\n  recoveryState: RecoveryState;\n}\n\n/**\n * Public Recovery API hook. Unlike the widget-internal useRecovery fetch, this\n * hook uses the host QueryClient and accepts React Query policies through\n * `query`. queryKey/queryFn and balance-shaping options remain SDK-owned.\n */\nexport function useRelayRecovery(\n  opts: UseRelayRecoveryOptions = {},\n): UseRelayRecoveryResult {\n  const optsRef = useRef(opts);\n  optsRef.current = opts;\n\n  const client = useMemo(() => {\n    if (opts.client) return opts.client;\n    if (!opts.apiBaseUrl) {\n      throw new Error(\n        \"useRelayRecovery: either `client` or `apiBaseUrl` must be provided\",\n      );\n    }\n    return createRelayClient({ baseUrl: opts.apiBaseUrl });\n  }, [opts.client, opts.apiBaseUrl]);\n\n  const recipient = opts.recipient;\n  const recipientKey = recipient && isAddress(recipient) ? recipient : undefined;\n  const target = opts.target;\n  const queryOptions = opts.query;\n  const clientId = useMemo(() => getClientId(client), [client]);\n  const queryKey = useMemo<RelayRecoveryQueryKey>(\n    () => [\"relay\", \"recovery\", clientId, recipientKey ?? null, target ?? null],\n    [clientId, recipientKey, target],\n  );\n\n  const recoveryQuery = useQuery({\n    retry: false,\n    ...queryOptions,\n    queryKey,\n    queryFn: async () => {\n      if (!recipientKey) {\n        throw new Error(\"useRelayRecovery: a valid EVM recipient is required\");\n      }\n      return client.getRecovery({ user: recipientKey, target });\n    },\n    // An invalid/missing address always wins over a caller-supplied policy.\n    enabled: recipientKey ? queryOptions?.enabled : false,\n  });\n\n  const handledErrorAtRef = useRef(0);\n  useEffect(() => {\n    const error = recoveryQuery.error;\n    const errorUpdatedAt = recoveryQuery.errorUpdatedAt;\n    if (!error || errorUpdatedAt === handledErrorAtRef.current) return;\n    handledErrorAtRef.current = errorUpdatedAt;\n    optsRef.current.onError?.(error);\n    if (error instanceof RelayApiError && error.status === 401) {\n      optsRef.current.onUnauthorized?.();\n    }\n  }, [recoveryQuery.error, recoveryQuery.errorUpdatedAt]);\n\n  const refresh = useCallback(() => {\n    void recoveryQuery.refetch();\n  }, [recoveryQuery.refetch]);\n\n  const actions = useRecoveryActions({\n    recipient: opts.recipient,\n    wallet: opts.wallet,\n    trustedFactories: opts.trustedFactories,\n    onError: opts.onError,\n    onRecoverySuccess: opts.onRecoverySuccess,\n    onChanged: refresh,\n  });\n\n  const info = recoveryQuery.data;\n  const recover = useCallback(\n    async (action: RecoveryAction, tokenAddress?: string) => {\n      if (!info) return;\n      await actions.recover(info, action, tokenAddress);\n    },\n    [actions, info],\n  );\n\n  return {\n    info,\n    loading: recoveryQuery.isLoading,\n    error: recoveryQuery.error ?? undefined,\n    isFetching: recoveryQuery.isFetching,\n    refresh,\n    recover,\n    recoveryState: actions.stateFor(info?.forwarder),\n  };\n}\n","\"use client\";\n\n// Ported from relay-protocol packages/relay-widget/src/components/StatusTracker.tsx\n// (v0.1.0). Mechanical substitution only: CSS Modules `styles.x` references\n// become literal \"rd-x\" class names (see ../styles/component.css's header\n// mapping table), the CSS Modules import becomes the plain side-effect import\n// below, and the wire types now come from ../lib/types. Everything else --\n// props, layout, and behavior -- is the original logic, untouched.\n//\n// The reference component polls GET /v1/orders/{id} itself via react-query;\n// this component is purely presentational and expects a caller to own that\n// poll (no react-query, per the package's peer-deps-only constraint) and\n// pass the latest result in as `order`/`error`. Not wired into\n// <RelayDeposit>'s default address-first flow (Task 4 uses OrdersList's\n// GET /v1/orders?user= list instead, since a reusable address has no single\n// \"the order\" to track) -- kept as an export for hosts that want a single\n// order's deposit -> bridge -> deliver stepper, e.g. for one row picked out\n// of that list.\nimport type { Order, StepStatus } from \"../lib/types\";\nimport \"../styles/component.css\";\n\nexport interface StatusTrackerProps {\n  orderId: string;\n  /** Latest polled order (steps/txs) from GET /v1/orders/{orderId}, owned by the caller. */\n  order?: Order;\n  /** Latest poll error, owned by the caller. */\n  error?: Error;\n}\n\n/** tx-hash link targets by GET /v1/orders/{id}'s txs.* keys. Best-effort --\n * bscBridge is always a BSC tx, crossFinalize/crossExecute always CROSS. */\nconst EXPLORER_BASE_URL: Record<keyof Order[\"txs\"], string> = {\n  bscBridge: \"https://bscscan.com/tx/\",\n  crossFinalize: \"https://scan.crosstoken.io/tx/\",\n  crossExecute: \"https://scan.crosstoken.io/tx/\",\n};\n\nconst FRIENDLY_TX_LABEL: Record<keyof Order[\"txs\"], string> = {\n  bscBridge: \"Bridge tx (BSC)\",\n  crossFinalize: \"Finalize tx (CROSS)\",\n  crossExecute: \"Execute tx (CROSS)\",\n};\n\nfunction stepGlyph(status: StepStatus): string {\n  if (status === \"done\") return \"✓\";\n  if (status === \"active\") return \"●\";\n  return \"\";\n}\n\nfunction isTerminalFailure(status: string): boolean {\n  return status === \"FAILED\" || status === \"REFUNDED\" || status === \"RELAY_REFUNDED\" || status === \"EXPIRED\";\n}\n\nfunction stepperItemClassName(status: StepStatus): string {\n  if (status === \"done\") return \"rd-stepper-item rd-stepper-item-done\";\n  if (status === \"active\") return \"rd-stepper-item rd-stepper-item-active\";\n  return \"rd-stepper-item rd-stepper-item-pending\";\n}\n\nexport function StatusTracker({ orderId, order, error }: StatusTrackerProps) {\n  // No first poll response yet (and no error either) -- mirrors the\n  // reference component's react-query `isLoading`.\n  const isLoading = !order && !error;\n\n  return (\n    <section className=\"rd-card\">\n      <div className=\"rd-card-header\">\n        <h2 className=\"rd-card-title\">4. Status</h2>\n        {order && (\n          <span\n            className={`rd-status-pill${\n              order.status === \"COMPLETED\"\n                ? \" rd-status-pill-success\"\n                : isTerminalFailure(order.status)\n                  ? \" rd-status-pill-failure\"\n                  : \"\"\n            }`}\n          >\n            {order.status}\n          </span>\n        )}\n      </div>\n\n      <p className=\"rd-card-hint\">Order {orderId}</p>\n\n      {isLoading && (\n        <p className=\"rd-muted\">\n          <span className=\"rd-spinner\" /> Loading status...\n        </p>\n      )}\n\n      {error && (\n        <div className=\"rd-callout rd-callout-danger\">\n          Couldn&apos;t load order status: {error.message}\n        </div>\n      )}\n\n      {order && (\n        <>\n          <ol className=\"rd-stepper\">\n            {order.steps.map((step, i) => (\n              <li key={step.key} className={stepperItemClassName(step.status)}>\n                <div className=\"rd-stepper-rail\">\n                  <span className=\"rd-stepper-dot\">{stepGlyph(step.status)}</span>\n                  {i < order.steps.length - 1 && <span className=\"rd-stepper-line\" />}\n                </div>\n                <div className=\"rd-stepper-body\">\n                  <div className=\"rd-stepper-label\">{step.label}</div>\n                  {step.key === \"bridge\" && order.txs.bscBridge && (\n                    <div className=\"rd-stepper-meta\">\n                      <TxLink orderTxs={order.txs} txKey=\"bscBridge\" />\n                    </div>\n                  )}\n                  {step.key === \"deliver\" && (order.txs.crossFinalize || order.txs.crossExecute) && (\n                    <div className=\"rd-stepper-meta\">\n                      {order.txs.crossFinalize && <TxLink orderTxs={order.txs} txKey=\"crossFinalize\" />}\n                      {order.txs.crossExecute && <TxLink orderTxs={order.txs} txKey=\"crossExecute\" />}\n                    </div>\n                  )}\n                </div>\n              </li>\n            ))}\n          </ol>\n\n          {isTerminalFailure(order.status) && (\n            <div className=\"rd-callout rd-callout-danger\">\n              This order did not complete successfully (status: {order.status}). If funds were deposited, refer to\n              the refund address on the order for recovery.\n            </div>\n          )}\n\n          {order.status === \"COMPLETED\" && (\n            <div className=\"rd-callout rd-callout-success\">crossd delivered. This order is complete.</div>\n          )}\n        </>\n      )}\n    </section>\n  );\n}\n\nfunction TxLink({ orderTxs, txKey }: { orderTxs: Order[\"txs\"]; txKey: keyof Order[\"txs\"] }) {\n  const hash = orderTxs[txKey];\n  if (!hash) return null;\n  return (\n    <a href={`${EXPLORER_BASE_URL[txKey]}${hash}`} target=\"_blank\" rel=\"noreferrer\">\n      {FRIENDLY_TX_LABEL[txKey]}: {hash.slice(0, 10)}...\n    </a>\n  );\n}\n","import type { BridgeToken } from \"../../bridge\";\nimport type {\n  GetOneUsdActionId,\n  GetOneUsdPairs,\n  GetOneUsdTokenRef,\n  ResolvedGetOneUsdRoute,\n} from \"../types\";\n\nexport function getOneUsdTokenKey(token: GetOneUsdTokenRef): string {\n  return `${token.chainId}:${token.address.toLowerCase()}`;\n}\n\nexport function matchesGetOneUsdToken(\n  token: BridgeToken,\n  ref: GetOneUsdTokenRef,\n): boolean {\n  return getOneUsdTokenKey(token) === getOneUsdTokenKey(ref);\n}\n\nexport function findGetOneUsdToken(\n  tokens: BridgeToken[],\n  ref?: GetOneUsdTokenRef,\n): BridgeToken | undefined {\n  if (!ref) return undefined;\n  return tokens.find((token) => matchesGetOneUsdToken(token, ref));\n}\n\n/** Resolve an action's exact token allowlist without mutating adapter data. */\nexport function resolveGetOneUsdRoute(\n  tokens: BridgeToken[],\n  pairs: GetOneUsdPairs | undefined,\n  action: Exclude<GetOneUsdActionId, \"transfer\">,\n): ResolvedGetOneUsdRoute {\n  if (!pairs) return { fromTokens: [], targetToken: undefined };\n\n  const fromRefs =\n    action === \"swap\" ? [pairs.swap.from] : pairs.bridge.from;\n  return {\n    fromTokens: fromRefs\n      .map((ref) => findGetOneUsdToken(tokens, ref))\n      .filter((token): token is BridgeToken => Boolean(token)),\n    targetToken: findGetOneUsdToken(tokens, pairs.target),\n  };\n}\n\nexport function isGetOneUsdTargetAvailable(\n  available: BridgeToken[],\n  target: BridgeToken,\n): boolean {\n  return available.some(\n    (token) =>\n      token.chainId === target.chainId &&\n      token.address.toLowerCase() === target.address.toLowerCase(),\n  );\n}\n","import { DAPP_UI_VERSION } from \"./version\";\n\n/**\n * S3 설정(apps.json)이 \"이 버전 미만이면 기능 끔\"을 지정할 수 있게 하는 비교기.\n *\n * 비교 대상은 **호스트가 설치한 dapp-ui 버전**(`DAPP_UI_VERSION`)이다. apps.json은\n * 런타임에 CDN에서 읽으므로 새 설정이 구버전 dapp-ui에도 그대로 전달된다 —\n * 즉 구버전에서 깨지는 기능을 배포 없이 끄려면 이 게이트가 필요하다.\n */\n\ntype Parsed = { parts: number[]; prerelease: string | null };\n\n/** \"1.3.14\" / \"1.3.14-beta.2\" / \"v1.3.14\" 를 비교 가능한 형태로 만든다. */\nfunction parse(version: string): Parsed | null {\n  const match = /^v?(\\d+(?:\\.\\d+)*)(?:-([0-9A-Za-z.-]+))?$/.exec(version.trim());\n  if (!match) return null;\n  return {\n    parts: match[1].split(\".\").map((part) => Number(part)),\n    prerelease: match[2] ?? null,\n  };\n}\n\n/** semver 순서: a < b 면 음수, 같으면 0, a > b 면 양수. 파싱 실패 시 `null`. */\nexport function compareVersions(a: string, b: string): number | null {\n  const left = parse(a);\n  const right = parse(b);\n  if (!left || !right) return null;\n\n  const length = Math.max(left.parts.length, right.parts.length);\n  for (let index = 0; index < length; index += 1) {\n    const diff = (left.parts[index] ?? 0) - (right.parts[index] ?? 0);\n    if (diff !== 0) return diff < 0 ? -1 : 1;\n  }\n\n  // 1.3.14-beta.1 < 1.3.14 (프리릴리스는 정식 릴리스보다 낮다).\n  if (left.prerelease === right.prerelease) return 0;\n  if (left.prerelease === null) return 1;\n  if (right.prerelease === null) return -1;\n  return left.prerelease < right.prerelease ? -1 : 1;\n}\n\n/**\n * `minVersion` 요구를 충족하는지. 요구가 없으면 통과.\n *\n * 값이 깨져 있으면(오타·잘못된 형식) **통과시킨다** — 설정 오타 하나로 모든\n * 사용자의 기능이 조용히 사라지는 쪽이 더 위험하다. apps.json 로드 실패 시\n * swap/bridge를 계속 노출하는 기존 정책과 같은 방향이다.\n */\nexport function meetsMinVersion(\n  minVersion?: string,\n  currentVersion: string = DAPP_UI_VERSION,\n): boolean {\n  if (!minVersion) return true;\n  const order = compareVersions(currentVersion, minVersion);\n  return order === null ? true : order >= 0;\n}\n","import type { AppsConfig } from \"../../../lib/remote-config\";\nimport { meetsMinVersion } from \"../../../lib/version-gate\";\nimport type { GetOneUsdActionId } from \"../types\";\n\nexport type GetOneUsdRemoteModes = NonNullable<\n  NonNullable<AppsConfig[\"getOneUsd\"]>[\"modes\"]\n>;\n\nexport type GetOneUsdRemoteMinVersions = NonNullable<\n  NonNullable<AppsConfig[\"getOneUsd\"]>[\"minVersions\"]\n>;\n\nconst REMOTE_MODE_KEYS: Record<\n  GetOneUsdActionId,\n  keyof GetOneUsdRemoteModes\n> = {\n  swap: \"swap\",\n  bridge: \"bridge\",\n  transfer: \"transferCrypto\",\n};\n\n/**\n * 목록에 보일지 여부. Swap/Bridge stay enabled when config is unavailable.\n * Transfer Crypto is opt-in and stays hidden until S3 explicitly enables it.\n */\nexport function isGetOneUsdActionVisible(\n  action: GetOneUsdActionId,\n  modes?: GetOneUsdRemoteModes,\n): boolean {\n  return modes?.[REMOTE_MODE_KEYS[action]] ?? action !== \"transfer\";\n}\n\n/**\n * 이 dapp-ui 버전에서 실행 가능한지. 미달이면 **숨기지 않고 비활성**으로 둔다 —\n * 항목이 조용히 사라지면 사용자는 기능이 없어진 줄 알지만, 비활성 + 안내면\n * \"업데이트하면 쓸 수 있다\"가 전달된다.\n */\nexport function isGetOneUsdActionSupported(\n  action: GetOneUsdActionId,\n  minVersions?: GetOneUsdRemoteMinVersions,\n): boolean {\n  return meetsMinVersion(minVersions?.[REMOTE_MODE_KEYS[action]]);\n}\n\n/** 노출 + 실행 가능(= 실제로 선택할 수 있는지). */\nexport function isGetOneUsdActionEnabled(\n  action: GetOneUsdActionId,\n  modes?: GetOneUsdRemoteModes,\n  minVersions?: GetOneUsdRemoteMinVersions,\n): boolean {\n  return (\n    isGetOneUsdActionVisible(action, modes) &&\n    isGetOneUsdActionSupported(action, minVersions)\n  );\n}\n","import type { Environment } from \"../../../types\";\nimport type { GlobalMenu } from \"../../app-launcher/types\";\n\nexport const ONE_USD_SITE_URLS: Record<Environment, string> = {\n  dev: \"https://stg.oneusd.money/\",\n  stage: \"https://stg.oneusd.money/\",\n  production: \"https://www.oneusd.money/\",\n};\n\nexport function isOneUsdHostname(hostname: string): boolean {\n  const normalized = hostname.trim().toLowerCase().replace(/\\.$/, \"\");\n  return normalized === \"oneusd.money\" || normalized.endsWith(\".oneusd.money\");\n}\n\nexport function resolveOneUsdSiteUrl(\n  config: GlobalMenu | null | undefined,\n  env: Environment,\n): string | undefined {\n  const item = config?.items?.find(\n    ({ id, type }) => id.toLowerCase() === \"oneusd\" || type.toLowerCase() === \"oneusd\",\n  );\n  return item?.url[env];\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { fetchCrossdAprPercent } from \"../../wallet-info/queries/use-crossd-apr\";\n\nexport function useGetOneUsdApr(enabled: boolean): number | null {\n  const [aprPercent, setAprPercent] = React.useState<number | null>(null);\n\n  React.useEffect(() => {\n    if (!enabled) return;\n    let alive = true;\n    void fetchCrossdAprPercent()\n      .then((value) => {\n        if (alive) setAprPercent(value);\n      })\n      .catch(() => {\n        // APR is supporting information; the Get ONEUSD action remains usable.\n      });\n    return () => {\n      alive = false;\n    };\n  }, [enabled]);\n\n  return aprPercent;\n}\n","import type {\n  BridgePathType,\n  BridgeSubmittedInfo,\n} from \"../../bridge\";\nimport type { GetOneUsdActionId } from \"../types\";\n\nfunction hasBridgeLeg(\n  pathType: BridgePathType | undefined,\n  action: GetOneUsdActionId,\n): boolean {\n  if (action === \"bridge\") return true;\n  return (\n    pathType === \"bridge\" ||\n    pathType === \"bridge-swap\" ||\n    pathType === \"swap-bridge\"\n  );\n}\n\nfunction formatTransactionAmount(value: string): string {\n  if (!value) return \"\";\n  const [integer = \"0\", decimal = \"\"] = value.replace(/,/g, \"\").split(\".\");\n  const normalizedInteger = integer.replace(/^0+(?=\\d)/, \"\") || \"0\";\n  const groupedInteger = normalizedInteger.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n  const trimmedDecimal = decimal.slice(0, 6).replace(/0+$/, \"\");\n  return trimmedDecimal ? `${groupedInteger}.${trimmedDecimal}` : groupedInteger;\n}\n\nfunction getChainLabel(chainId: number): string {\n  if (chainId === 56 || chainId === 97) return \"BSC\";\n  if (chainId === 612044 || chainId === 612055 || chainId === 5555) {\n    return \"ONEchain\";\n  }\n  return `Chain ${chainId}`;\n}\n\nexport function getTransactionPendingLabel(\n  info: BridgeSubmittedInfo,\n  action: GetOneUsdActionId,\n): \"Bridging\" | \"Swapping\" {\n  return hasBridgeLeg(info.pathType, action) ? \"Bridging\" : \"Swapping\";\n}\n\nexport function getTransactionSuccessNote(\n  info: BridgeSubmittedInfo,\n  action: GetOneUsdActionId,\n): string {\n  const amount = formatTransactionAmount(info.toAmount);\n  if (hasBridgeLeg(info.pathType, action)) {\n    const arriving = `arriving on ${getChainLabel(info.toToken.chainId)}`;\n    return amount ? `${amount} ${arriving}` : arriving;\n  }\n  return amount ? `${amount} Swapped` : \"Transaction completed\";\n}\n","import type { SVGProps } from \"react\";\n\ntype IconProps = SVGProps<SVGSVGElement>;\n\nexport function SwapActionIcon(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" {...props}>\n      <path d=\"m7 3-3 3 3 3M4 6h13a3 3 0 0 1 3 3v1M17 21l3-3-3-3m3 3H7a3 3 0 0 1-3-3v-1\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n    </svg>\n  );\n}\n\n/** 체인 링크 — Swap의 양방향 화살표와 실루엣이 겹치지 않게 고른 형태. */\nexport function BridgeActionIcon(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" {...props}>\n      <path d=\"M10 13a4 4 0 0 0 5.66 0l2.83-2.83a4 4 0 0 0-5.66-5.66L11.4 5.94\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" />\n      <path d=\"M14 11a4 4 0 0 0-5.66 0L5.51 13.8a4 4 0 0 0 5.66 5.66l1.42-1.41\" stroke=\"currentColor\" strokeWidth=\"1.7\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\n/** QR 코드 — 이 액션의 실제 화면(입금 주소 QR)을 그대로 가리킨다. */\nexport function TransferActionIcon(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" {...props}>\n      <rect x=\"4\" y=\"4\" width=\"6\" height=\"6\" rx=\"1.4\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n      <rect x=\"14\" y=\"4\" width=\"6\" height=\"6\" rx=\"1.4\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n      <rect x=\"4\" y=\"14\" width=\"6\" height=\"6\" rx=\"1.4\" stroke=\"currentColor\" strokeWidth=\"1.7\" />\n      <path d=\"M14 14h2v2h-2zM18 18h2v2h-2z\" fill=\"currentColor\" />\n    </svg>\n  );\n}\n\nexport function ChevronRightActionIcon(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" {...props}>\n      <path d=\"m9 6 6 6-6 6\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n    </svg>\n  );\n}\n\nexport function CloseActionIcon(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\" {...props}>\n      <path d=\"m6 6 12 12M18 6 6 18\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n","import styleInject from '#style-inject';styleInject(\".gou-fab{--gou-collapse-duration: .24s;--gou-collapse-ease: cubic-bezier(.32, .72, 0, 1);position:fixed;right:var(--gou-right, 2rem);bottom:calc(env(safe-area-inset-bottom,0px) + var(--gou-bottom, 2rem));z-index:var(--gou-z-index, 40);display:inline-flex;width:max-content;align-items:center;gap:8px;padding:4px 20px 4px 4px;border:0;border-radius:9999px;background:#fff;color:#121212;box-shadow:0 4px 20px #00000073;font:inherit;cursor:pointer;transition:gap var(--gou-collapse-duration) var(--gou-collapse-ease),padding var(--gou-collapse-duration) var(--gou-collapse-ease),transform .15s ease,background .15s ease;-webkit-tap-highlight-color:transparent}[data-theme=light] .gou-fab{background:#121212;color:#fff;box-shadow:0 4px 20px #00000040}.gou-fab:active:not(:disabled){transform:scale(.96)}.gou-fab:focus-visible{outline:3px solid #f3742e;outline-offset:3px}.gou-fab[data-icon-only=true]{gap:0;padding:0}.gou-fab:disabled{cursor:default}.gou-fab[data-trigger-mode=button]{position:relative;right:auto;bottom:auto;z-index:auto;vertical-align:middle}[data-gou-mode=button]{display:inline-block}.gou-fab-status{pointer-events:none;cursor:default}.gou-fab-icon-frame{position:relative;display:inline-flex;width:38px;height:38px;flex-shrink:0;border-radius:9999px;transition:width var(--gou-collapse-duration) var(--gou-collapse-ease),height var(--gou-collapse-duration) var(--gou-collapse-ease)}.gou-fab-icon{display:block;width:100%;height:100%;border-radius:inherit;object-fit:contain}.gou-fab[data-icon-only=true] .gou-fab-icon-frame{width:52px;height:52px}.gou-fab-progress-ring{position:absolute;inset:-2px;border:2px solid rgba(243,116,46,.25);border-top-color:#f3742e;border-radius:9999px;animation:gou-spin .9s linear infinite}.gou-fab-label{overflow:hidden;max-width:180px;opacity:1;font-size:14px;font-weight:600;line-height:1.5;white-space:nowrap;transition:max-width var(--gou-collapse-duration) var(--gou-collapse-ease),opacity calc(var(--gou-collapse-duration) * .6) var(--gou-collapse-ease)}.gou-fab[data-icon-only=true] .gou-fab-label{max-width:0;opacity:0}.gou-fab[data-tx-phase] .gou-fab-label{max-width:260px}.gou-fab-trigger-copy{display:flex;flex-direction:column;align-items:flex-start;justify-content:center;line-height:1.25}.gou-fab-apr{color:var(--ds-system-orange-high, #ff7a3d);font-size:11px;font-weight:600;line-height:1.25}.gou-fab[data-tx-phase=success]{animation:gou-result-success 1.2s ease-out}.gou-fab[data-tx-phase=failure]{animation:gou-result-failure 1.2s ease-out}.gou-fab-dots>span{animation:gou-status-dot 1.4s ease-in-out infinite}.gou-fab-dots>span:nth-child(2){animation-delay:.2s}.gou-fab-dots>span:nth-child(3){animation-delay:.4s}.gou-tooltip{position:absolute;right:0;bottom:calc(100% + 8px);pointer-events:none;padding:6px 10px;border-radius:8px;background:#121212;color:#fff;box-shadow:0 8px 24px #0000002e;font-size:12px;font-weight:500;opacity:0;white-space:nowrap;transition:opacity .2s ease}[data-theme=dark] .gou-tooltip{background:#f5f5f5;color:#121212}.gou-fab:hover .gou-tooltip{opacity:1}.gou-shell{--ds-bg-default: #1c1c1d;--ds-bg-dim: #181819;--ds-surface-default: #1c1c1d;--ds-surface-medium: #212224;--ds-surface-high: rgba(255, 255, 255, .08);--ds-border-default: rgba(255, 255, 255, .1);--ds-content-highest: #ffffff;--ds-content-high: rgba(255, 255, 255, .85);--ds-content-medium: rgba(255, 255, 255, .65);--ds-content-low: rgba(255, 255, 255, .4);--ds-accent-primary-default: #f3742e;--ds-accent-primary-high: #f3742e;--ds-accent-primary-low: rgba(243, 116, 46, .1);--ds-accent-primary-on: #ffffff;--gou-bg: #1c1c1d;--gou-card: #212224;--gou-card-hover: rgba(255, 255, 255, .08);--gou-border: rgba(255, 255, 255, .1);--gou-text: #ffffff;--gou-muted: rgba(255, 255, 255, .65);background:var(--gou-bg);color:var(--gou-text)}.gou-shell[data-theme=light]{--ds-bg-default: #ffffff;--ds-bg-dim: #fafbfd;--ds-surface-default: #ffffff;--ds-surface-medium: rgba(0, 0, 0, .01);--ds-surface-high: rgba(0, 0, 0, .03);--ds-border-default: rgba(0, 0, 0, .06);--ds-content-highest: #000000;--ds-content-high: rgba(0, 0, 0, .85);--ds-content-medium: rgba(0, 0, 0, .65);--ds-content-low: rgba(0, 0, 0, .4);--gou-bg: #ffffff;--gou-card: #ffffff;--gou-card-hover: rgba(0, 0, 0, .03);--gou-border: rgba(0, 0, 0, .06);--gou-text: #000000;--gou-muted: rgba(0, 0, 0, .65)}.gou-chooser{overflow:hidden}.gou-chooser-body{position:relative;display:flex;flex-direction:column;gap:24px;padding:24px}.gou-close{position:absolute;top:16px;right:16px;display:inline-flex;width:32px;height:32px;align-items:center;justify-content:center;padding:0;border:0;border-radius:9999px;background:transparent;color:var(--gou-muted);cursor:pointer}.gou-close:hover{background:var(--gou-card-hover);color:var(--gou-text)}.gou-close svg{width:20px;height:20px}.gou-intro{display:flex;flex-direction:column;gap:6px;padding:0 32px 0 0;text-align:left}.gou-intro h2{margin:0;color:var(--gou-text);font-size:20px;font-weight:650;line-height:1.6}.gou-intro-link{display:inline-flex;width:fit-content;align-items:center;gap:4px;color:var(--ds-accent-primary-default, #f3742e);font-size:14px;font-weight:600;line-height:1.5;text-decoration:none}.gou-intro-link-icon{flex-shrink:0}.gou-intro-link>span{text-decoration:underline;text-underline-offset:3px}.gou-intro-link:focus-visible{outline:2px solid #f3742e;outline-offset:3px;border-radius:4px}.gou-actions{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}.gou-action{display:flex;width:100%;min-width:0;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--gou-border);border-radius:12px;background:var(--gou-card);color:var(--gou-text);font:inherit;text-align:left;cursor:pointer;transition:border-color .15s ease,background .15s ease}.gou-action:hover:not(:disabled){border-color:#f3742e;background:var(--gou-card-hover)}.gou-action:disabled{cursor:not-allowed;opacity:.5}.gou-action:focus-visible{outline:2px solid #f3742e;outline-offset:2px}.gou-action-icon{display:inline-flex;width:32px;height:32px;flex-shrink:0;align-items:center;justify-content:center;border:1px solid var(--gou-border);border-radius:9px;color:var(--gou-text);transition:border-color .15s ease,color .15s ease}.gou-action:hover:not(:disabled) .gou-action-icon{border-color:var(--ds-accent-primary-default, #f3742e);color:var(--ds-accent-primary-default, #f3742e)}.gou-action-icon svg{width:17px;height:17px}.gou-action-copy{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}.gou-action-title{display:flex;min-width:0;align-items:center;gap:6px;font-size:16px;font-weight:650}.gou-action-description{color:var(--gou-muted);font-size:14px;line-height:1.45}.gou-action-chevron{width:18px;height:18px;flex-shrink:0;color:var(--gou-muted)}.gou-badge{flex-shrink:0;padding:2px 6px;border-radius:5px;background:#f3742e24;color:#f3742e;font-size:10px;font-weight:650}.gou-badge[data-tone=muted]{background:var(--gou-card-hover);color:var(--gou-muted)}.gou-execution{height:auto;max-height:90dvh;overflow:hidden}.gou-execution .bf-root{width:100%;max-height:90dvh}.gou-connect-required,.gou-route-state{display:flex;min-height:360px;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:32px;text-align:center}.gou-connect-required h3,.gou-route-state h3{margin:0;color:var(--gou-text);font-size:18px}.gou-connect-required p,.gou-route-state p{max-width:320px;margin:0;color:var(--gou-muted);font-size:14px;line-height:1.5}.gou-lock{font-size:28px}.gou-connect-required button{width:100%;max-width:320px;margin-top:10px;padding:12px 16px;border:0;border-radius:12px;background:var(--ds-accent-primary-default, #00ceb2);color:var(--ds-accent-primary-on, #001b18);font:inherit;font-size:16px;font-weight:650;cursor:pointer}.gou-spinner{width:28px;height:28px;border:3px solid var(--gou-border);border-top-color:var(--ds-accent-primary-default, #00ceb2);border-radius:9999px;animation:gou-spin .8s linear infinite}@keyframes gou-spin{to{transform:rotate(360deg)}}@keyframes gou-status-dot{0%,60%,to{opacity:.2}30%{opacity:1}}@keyframes gou-result-success{0%{outline:0 solid rgba(30,202,119,0)}30%{outline:3px solid rgba(30,202,119,.75)}to{outline:0 solid rgba(30,202,119,0)}}@keyframes gou-result-failure{0%{outline:0 solid rgba(255,60,83,0)}30%{outline:3px solid rgba(255,60,83,.75)}to{outline:0 solid rgba(255,60,83,0)}}@media(max-width:767px){.gou-fab{right:var(--gou-right-mobile, 1rem)}.gou-fab-icon-frame{width:32px;height:32px}.gou-fab[data-icon-only=true] .gou-fab-icon-frame{width:40px;height:40px}.gou-chooser-body{padding:24px 16px 28px}.gou-execution{height:auto;max-height:70dvh}.gou-execution .bf-root{max-height:calc(70dvh - 24px)}}@media(prefers-reduced-motion:reduce){.gou-fab,.gou-fab-icon-frame,.gou-fab-label,.gou-tooltip{transition:none}.gou-spinner,.gou-fab-progress-ring{animation-duration:1.6s}.gou-fab[data-tx-phase=success],.gou-fab[data-tx-phase=failure],.gou-fab-dots>span{animation:none}}\\n\")","\"use client\";\n\nimport * as React from \"react\";\nimport * as Dialog from \"@radix-ui/react-dialog\";\nimport { Drawer } from \"vaul\";\nimport { WalletConnectModalContext } from \"../providers/wallet-connect-modal-provider\";\nimport { useHistoryDismiss } from \"../../../hooks/use-history-dismiss\";\nimport { useMediaQuery } from \"../../../hooks/use-media-query\";\nimport { useWalletDetect } from \"../hooks/use-wallet-detect\";\nimport { WalletConnectModalTrigger } from \"./WalletConnectModalTrigger\";\nimport { WalletConnectModalContent } from \"./WalletConnectModalContent\";\nimport type { WalletConnectModalProps } from \"../types\";\nimport type { SocialId, WalletId } from \"../constants\";\nimport { DEFAULT_TERMS_URL, DEFAULT_PRIVACY_URL } from \"../../../lib/constants\";\nimport \"../styles/component.css\";\nimport \"../styles/keyframes.css\";\n\nconst EMPTY_SOCIAL_PROVIDERS = {} as const;\n\nfunction WalletConnectModalRoot({\n  wallets,\n  socialProviders = EMPTY_SOCIAL_PROVIDERS,\n  termsUrl,\n  privacyUrl,\n  theme = \"dark\",\n  mobileBreakpoint = 768,\n  drawerDirection = \"bottom\",\n  dialogWidth,\n  drawerMaxWidth,\n  drawerMinWidth,\n  style,\n  open: openProp,\n  onOpenChange,\n  children,\n}: WalletConnectModalProps) {\n  const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint}px)`);\n  const [internalOpen, setInternalOpen] = React.useState(false);\n\n  const isControlled = openProp !== undefined;\n  const open = isControlled ? openProp : internalOpen;\n\n  const handleOpenChange = React.useCallback(\n    (value: boolean) => {\n      if (!isControlled) setInternalOpen(value);\n      onOpenChange?.(value);\n    },\n    [isControlled, onOpenChange],\n  );\n\n  const close = React.useCallback(\n    () => handleOpenChange(false),\n    [handleOpenChange],\n  );\n\n  // Android 백버튼 / iOS 백스와이프로 닫히게 한다.\n  useHistoryDismiss(open, close);\n\n  const { isDetected } = useWalletDetect();\n\n  const activeWalletIds = React.useMemo(\n    () => Object.keys(wallets) as WalletId[],\n    [wallets],\n  );\n\n  const activeSocialIds = React.useMemo(\n    () => Object.keys(socialProviders) as SocialId[],\n    [socialProviders],\n  );\n\n  const mergedStyle = React.useMemo(() => {\n    const sizeVars: Record<string, string> = {};\n    if (dialogWidth) sizeVars[\"--wcm-dialog-width\"] = dialogWidth;\n    if (drawerMaxWidth) sizeVars[\"--wcm-drawer-max-width\"] = drawerMaxWidth;\n    if (drawerMinWidth) sizeVars[\"--wcm-drawer-min-width\"] = drawerMinWidth;\n    return { ...sizeVars, ...style };\n  }, [dialogWidth, drawerMaxWidth, drawerMinWidth, style]);\n\n  const contextValue = React.useMemo(\n    () => ({\n      theme,\n      isMobile,\n      open,\n      setOpen: handleOpenChange,\n      close,\n      wallets,\n      activeWalletIds,\n      socialProviders,\n      activeSocialIds,\n      // 미설정 시 dapp-ui 공통 문서로 폴백해 약관/정책 링크가 항상 노출되게 한다.\n      termsUrl: termsUrl ?? DEFAULT_TERMS_URL,\n      privacyUrl: privacyUrl ?? DEFAULT_PRIVACY_URL,\n      isDetected,\n      portalStyle: mergedStyle,\n    }),\n    [\n      theme,\n      isMobile,\n      open,\n      handleOpenChange,\n      close,\n      wallets,\n      activeWalletIds,\n      socialProviders,\n      activeSocialIds,\n      termsUrl,\n      privacyUrl,\n      isDetected,\n      mergedStyle,\n    ],\n  );\n\n  const wrapper = (\n    <div data-theme={theme} style={mergedStyle}>\n      {children}\n    </div>\n  );\n\n  if (isMobile) {\n    return (\n      <WalletConnectModalContext.Provider value={contextValue}>\n        <Drawer.Root\n          key=\"drawer\"\n          direction={drawerDirection}\n          open={open}\n          onOpenChange={handleOpenChange}\n          noBodyStyles\n        >\n          {wrapper}\n        </Drawer.Root>\n      </WalletConnectModalContext.Provider>\n    );\n  }\n\n  return (\n    <WalletConnectModalContext.Provider value={contextValue}>\n      <Dialog.Root open={open} onOpenChange={handleOpenChange}>\n        {wrapper}\n      </Dialog.Root>\n    </WalletConnectModalContext.Provider>\n  );\n}\n\nexport const WalletConnectModal = Object.assign(WalletConnectModalRoot, {\n  Trigger: WalletConnectModalTrigger,\n  Content: WalletConnectModalContent,\n});\n","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport type { SocialHandlers, WalletHandlers } from \"../types\";\nimport type { SocialId, WalletId } from \"../constants\";\n\nexport interface WalletConnectModalContextValue {\n  theme: \"dark\" | \"light\";\n  isMobile: boolean;\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  close: () => void;\n  wallets: WalletHandlers;\n  activeWalletIds: WalletId[];\n  socialProviders: SocialHandlers;\n  activeSocialIds: SocialId[];\n  termsUrl?: string;\n  privacyUrl?: string;\n  isDetected: (rdns: string) => boolean;\n  portalStyle?: CSSProperties;\n}\n\nexport const WalletConnectModalContext =\n  createContext<WalletConnectModalContextValue | null>(null);\n\nexport function useWalletConnectModalContext() {\n  const ctx = useContext(WalletConnectModalContext);\n  if (!ctx) {\n    throw new Error(\n      \"WalletConnectModal compound components must be used within <WalletConnectModal>\",\n    );\n  }\n  return ctx;\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\n\ninterface DetectedWallet {\n  rdns: string;\n  name: string;\n  icon?: string;\n}\n\ninterface WalletDetectResult {\n  wallets: DetectedWallet[];\n  isDetected: (rdns: string) => boolean;\n  isLoading: boolean;\n}\n\nexport function useWalletDetect(): WalletDetectResult {\n  const [wallets, setWallets] = useState<DetectedWallet[]>([]);\n  const [isLoading, setIsLoading] = useState(true);\n\n  useEffect(() => {\n    if (typeof window === \"undefined\") {\n      setIsLoading(false);\n      return;\n    }\n\n    const detected: DetectedWallet[] = [];\n\n    const handleAnnouncement = (event: CustomEvent) => {\n      const { info } = event.detail;\n      if (info?.rdns && !detected.some((w) => w.rdns === info.rdns)) {\n        detected.push({\n          rdns: info.rdns,\n          name: info.name,\n          icon: info.icon,\n        });\n        setWallets([...detected]);\n      }\n    };\n\n    window.addEventListener(\n      \"eip6963:announceProvider\",\n      handleAnnouncement as EventListener,\n    );\n    window.addEventListener(\n      \"eip6963:crossAnnounceProvider\",\n      handleAnnouncement as EventListener,\n    );\n\n    window.dispatchEvent(new Event(\"eip6963:requestProvider\"));\n    window.dispatchEvent(new Event(\"eip6963:crossRequestProvider\"));\n\n    const timeout = setTimeout(() => setIsLoading(false), 500);\n\n    return () => {\n      window.removeEventListener(\n        \"eip6963:announceProvider\",\n        handleAnnouncement as EventListener,\n      );\n      window.removeEventListener(\n        \"eip6963:crossAnnounceProvider\",\n        handleAnnouncement as EventListener,\n      );\n      clearTimeout(timeout);\n    };\n  }, []);\n\n  const isDetected = useCallback(\n    (rdns: string) => wallets.some((w) => w.rdns === rdns),\n    [wallets],\n  );\n\n  return { wallets, isDetected, isLoading };\n}\n","\"use client\";\n\nimport * as Dialog from \"@radix-ui/react-dialog\";\nimport { Drawer } from \"vaul\";\nimport { useWalletConnectModalContext } from \"../providers/wallet-connect-modal-provider\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\nimport type { WalletConnectModalTriggerProps } from \"../types\";\n\nexport function WalletConnectModalTrigger({\n  asChild,\n  children,\n}: WalletConnectModalTriggerProps) {\n  const { isMobile } = useWalletConnectModalContext();\n  const Trigger = isMobile ? Drawer.Trigger : Dialog.Trigger;\n\n  return (\n    <TrackingBoundary feature=\"wallet_connect\">\n      <Trigger asChild={asChild ?? children != null}>\n        {children ?? (\n          <button type=\"button\" data-track=\"open\">\n            Connect Wallet\n          </button>\n        )}\n      </Trigger>\n    </TrackingBoundary>\n  );\n}\n","\"use client\";\n\nimport { useEffect, type ReactNode } from \"react\";\nimport { WALLET_LABELS } from \"@nexus-cross/crossx-design-system\";\nimport * as Dialog from \"@radix-ui/react-dialog\";\nimport { Drawer } from \"vaul\";\nimport { useWalletConnectModalContext } from \"../providers/wallet-connect-modal-provider\";\nimport { resolveSocial, resolveWallet } from \"../constants\";\nimport { recordSelectedWallet } from \"../last-selected\";\nimport { CloseIcon } from \"./icons\";\nimport { cn } from \"../../../lib/utils\";\nimport {\n  TrackingBoundary,\n  normalizeFailureReason,\n  trackDappUiFunnel,\n} from \"../../../lib/sentry/index\";\nimport type {\n  SocialConfig,\n  WalletConnectModalContentProps,\n  WalletConfig,\n} from \"../types\";\n\nfunction runConnectHandler(\n  id: string,\n  handler: (() => void | Promise<void>) | undefined,\n) {\n  trackDappUiFunnel(\"connect\", \"wallet_selected\", { tags: { item_id: id } });\n  // WalletInfo 계정 라벨 폴백용. 소셜 / 지갑 선택이 모두 이 함수를 지나므로\n  // 여기 한 곳에서 기록한다.\n  recordSelectedWallet(id);\n  const result = handler?.();\n  if (result instanceof Promise) {\n    result.then(\n      () => trackDappUiFunnel(\"connect\", \"connected\"),\n      (error) =>\n        trackDappUiFunnel(\"connect\", \"connected\", {\n          status: \"failure\",\n          reason: normalizeFailureReason(error),\n        }),\n    );\n  }\n}\n\nfunction WalletItem({\n  wallet,\n  detected,\n  onConnect,\n}: {\n  wallet: WalletConfig;\n  detected: boolean;\n  onConnect: () => void;\n}) {\n  const handleClick = () => {\n    if (wallet.installUrl && !detected) {\n      window.open(wallet.installUrl, \"_blank\", \"noopener,noreferrer\");\n      return;\n    }\n    onConnect();\n  };\n\n  const Icon = wallet.icon;\n  const featured = wallet.featured === true;\n\n  return (\n    <button\n      type=\"button\"\n      className={cn(\"wcm-wallet-item\", featured && \"wcm-wallet-item--featured\")}\n      data-track=\"wallet_item_click\"\n      data-track-id={wallet.id}\n      onClick={handleClick}\n    >\n      <div className=\"wcm-wallet-item-left\">\n        <div className=\"wcm-wallet-icon\">\n          <Icon />\n        </div>\n        <div className=\"wcm-wallet-name-row\">\n          <span className=\"wcm-wallet-name\">{wallet.name}</span>\n          {wallet.badge && (\n            <span className=\"wcm-wallet-badge\">{wallet.badge}</span>\n          )}\n        </div>\n      </div>\n      {wallet.installUrl &&\n        (detected ? (\n          <span className=\"wcm-installed-label\">Installed</span>\n        ) : (\n          <span className=\"wcm-install-btn\">Install</span>\n        ))}\n    </button>\n  );\n}\n\nfunction SocialItem({\n  social,\n  onSignIn,\n}: {\n  social: SocialConfig;\n  onSignIn: () => void;\n}) {\n  const Icon = social.icon;\n  const label = `Sign in with ${social.name}`;\n  return (\n    <button\n      type=\"button\"\n      className=\"wcm-social-item\"\n      data-provider={social.id}\n      data-track=\"social_provider_click\"\n      data-track-id={social.id}\n      onClick={onSignIn}\n      aria-label={label}\n    >\n      <span className=\"wcm-social-icon\">\n        <Icon />\n      </span>\n      <span className=\"wcm-social-name\">{label}</span>\n    </button>\n  );\n}\n\nfunction ModalBody() {\n  const {\n    wallets,\n    activeWalletIds,\n    socialProviders,\n    activeSocialIds,\n    termsUrl,\n    privacyUrl,\n    isDetected,\n    close,\n    isMobile,\n  } = useWalletConnectModalContext();\n\n  const visibleIds = activeWalletIds.filter((id) => {\n    const v = resolveWallet(id).visibility;\n    if (!v || v === \"always\") return true;\n    if (v === \"desktop-only\") return !isMobile;\n    if (v === \"mobile-only\") return isMobile;\n    return true;\n  });\n\n  const hasSocial = activeSocialIds.length > 0;\n  const hasWallets = visibleIds.length > 0;\n\n  return (\n    <>\n      <div className=\"wcm-header\">\n        <div className=\"wcm-header-text\">\n          <h2 className=\"wcm-title\">{WALLET_LABELS.default}</h2>\n          {!isMobile && (\n            <p className=\"wcm-subtitle\">\n              Sign in instantly with your social account\n            </p>\n          )}\n        </div>\n        <button\n          type=\"button\"\n          className=\"wcm-close-btn\"\n          data-track=\"close\"\n          onClick={close}\n          aria-label=\"Close\"\n        >\n          <CloseIcon />\n        </button>\n      </div>\n\n      <div className=\"wcm-body\">\n        {hasSocial && (\n          <>\n            {!isMobile && (\n              <div className=\"wcm-section-header\">\n                <span className=\"wcm-section-title\">\n                  Social Account Wallet Connect\n                </span>\n                <span className=\"wcm-recommended-badge\">Recommended</span>\n              </div>\n            )}\n            <div className=\"wcm-social-grid\">\n              {activeSocialIds.map((id) => {\n                const social = resolveSocial(id);\n                return (\n                  <SocialItem\n                    key={id}\n                    social={social}\n                    onSignIn={() => {\n                      runConnectHandler(id, socialProviders[id]);\n                      close();\n                    }}\n                  />\n                );\n              })}\n            </div>\n            {hasWallets && (\n              <div className=\"wcm-separator\">\n                <div className=\"wcm-separator-line\" />\n                <span className=\"wcm-separator-text\">Other Wallets</span>\n                <div className=\"wcm-separator-line\" />\n              </div>\n            )}\n          </>\n        )}\n\n        {hasWallets && (\n          <div className=\"wcm-wallet-list\">\n            {visibleIds.map((id) => {\n              const wallet = resolveWallet(id);\n              const detected = wallet.rdns ? isDetected(wallet.rdns) : false;\n              return (\n                <WalletItem\n                  key={id}\n                  wallet={wallet}\n                  detected={detected}\n                  onConnect={() => {\n                    runConnectHandler(id, wallets[id]);\n                    close();\n                  }}\n                />\n              );\n            })}\n          </div>\n        )}\n\n        <p className=\"wcm-terms\">\n          By continuing, you agree to NEXUS{\" \"}\n          <TermsLink href={termsUrl} track=\"terms_link\">\n            Terms of Service\n          </TermsLink>{\" \"}\n          and consent to its{\" \"}\n          <TermsLink href={privacyUrl} track=\"privacy_link\">\n            Privacy Policy\n          </TermsLink>\n          .\n        </p>\n      </div>\n    </>\n  );\n}\n\nfunction TermsLink({\n  href,\n  track,\n  children,\n}: {\n  href?: string;\n  track: string;\n  children: ReactNode;\n}) {\n  if (!href) return <span className=\"wcm-terms-link\">{children}</span>;\n  return (\n    <a\n      className=\"wcm-terms-link\"\n      href={href}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      data-track={track}\n    >\n      {children}\n    </a>\n  );\n}\n\nexport function WalletConnectModalContent({\n  className,\n}: WalletConnectModalContentProps) {\n  const { isMobile, theme, portalStyle, open } = useWalletConnectModalContext();\n\n  useEffect(() => {\n    if (open) trackDappUiFunnel(\"connect\", \"opened\");\n  }, [open]);\n\n  if (isMobile) {\n    return (\n      <TrackingBoundary feature=\"wallet_connect\">\n        <Drawer.Portal>\n          <Drawer.Overlay className=\"wcm-overlay\" />\n          <Drawer.Content\n            className={cn(\"wcm-drawer-content\", className)}\n            data-theme={theme}\n            style={portalStyle}\n          >\n            <Drawer.Title className=\"wcm-sr-only\">Connect Wallet</Drawer.Title>\n            <div className=\"wcm-drawer-handle\" />\n            <div className=\"wcm-drawer-body\">\n              <ModalBody />\n            </div>\n          </Drawer.Content>\n        </Drawer.Portal>\n      </TrackingBoundary>\n    );\n  }\n\n  return (\n    <TrackingBoundary feature=\"wallet_connect\">\n      <Dialog.Portal>\n        <Dialog.Overlay className=\"wcm-overlay\" />\n        <Dialog.Content\n          className={cn(\"wcm-dialog-content\", className)}\n          data-theme={theme}\n          style={portalStyle}\n        >\n          <Dialog.Title className=\"wcm-sr-only\">Connect Wallet</Dialog.Title>\n          <Dialog.Description className=\"wcm-sr-only\">\n            Sign in with a social provider or pick a wallet to connect.\n          </Dialog.Description>\n          <div className=\"wcm-dialog-body\">\n            <ModalBody />\n          </div>\n        </Dialog.Content>\n      </Dialog.Portal>\n    </TrackingBoundary>\n  );\n}\n","import styleInject from '#style-inject';styleInject(\".wcm-overlay{position:fixed;inset:0;z-index:100000;background:var(--ds-overlay-dim, rgba(0, 0, 0, .5));-webkit-backdrop-filter:var(--ds-bg-blur-7, blur(3.5px));backdrop-filter:var(--ds-bg-blur-7, blur(3.5px))}.wcm-dialog-content{-webkit-font-smoothing:antialiased;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:100001;width:var(--wcm-dialog-width, 480px);max-height:90vh;overflow:hidden;display:flex;flex-direction:column;background:var(--ds-surface-default, #ffffff);border:1px solid var(--ds-border-default, #00000012);border-radius:24px;box-shadow:var(--ds-shadow-modal, 0 4px 30px -1px rgba(0, 0, 0, .15));outline:none;animation:wcm-dialog-show .2s ease-out;font-family:inherit}.wcm-dialog-content[data-state=closed]{animation:wcm-dialog-hide .15s ease-in}.wcm-dialog-body,.wcm-drawer-body{scrollbar-width:thin;scrollbar-color:var(--ds-border-medium, rgba(0, 0, 0, .18)) transparent}.wcm-dialog-body::-webkit-scrollbar,.wcm-drawer-body::-webkit-scrollbar{width:4px}.wcm-dialog-body::-webkit-scrollbar-track,.wcm-drawer-body::-webkit-scrollbar-track{background:transparent}.wcm-dialog-body::-webkit-scrollbar-thumb,.wcm-drawer-body::-webkit-scrollbar-thumb{background:var(--ds-border-medium, rgba(0, 0, 0, .18));border-radius:4px}.wcm-drawer-content{-webkit-font-smoothing:antialiased;position:fixed;bottom:0;left:0;right:0;z-index:100001;width:100%;max-width:var(--wcm-drawer-max-width, none);min-width:var(--wcm-drawer-min-width, 0);max-height:85dvh;margin:0 auto;display:flex;flex-direction:column;background:var(--ds-surface-default, #ffffff);border:1px solid var(--ds-border-default, #00000012);border-top-left-radius:16px;border-top-right-radius:16px;outline:none;font-family:inherit;box-sizing:border-box}.wcm-drawer-content[data-vaul-drawer-direction=right]{inset:0 0 0 auto;max-height:100vh;width:var(--wcm-drawer-max-width, 400px);max-width:100vw;border-radius:16px 0 0 16px}.wcm-drawer-content[data-vaul-drawer-direction=left]{inset:0 auto 0 0;max-height:100vh;width:var(--wcm-drawer-max-width, 400px);max-width:100vw;border-radius:0 16px 16px 0}.wcm-drawer-content[data-vaul-drawer-direction=top]{inset:0 0 auto;border-radius:0 0 16px 16px}.wcm-drawer-handle{width:60px;height:4px;border-radius:999px;background:var(--ds-border-medium, rgba(0, 0, 0, .18));margin:12px auto 8px;flex-shrink:0}.wcm-drawer-content[data-vaul-drawer-direction=left] .wcm-drawer-handle,.wcm-drawer-content[data-vaul-drawer-direction=right] .wcm-drawer-handle{display:none}.wcm-dialog-body,.wcm-drawer-body{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden}.wcm-drawer-body{overscroll-behavior:contain}.wcm-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:24px;border-bottom:1px solid var(--ds-border-default, rgba(0, 0, 0, .07))}.wcm-header-text{display:flex;flex-direction:column;gap:4px;min-width:0}.wcm-title{font-family:Inter,sans-serif;font-weight:600;font-size:20px;line-height:1.25;letter-spacing:0;color:var(--ds-content-highest, #000000);margin:0}.wcm-subtitle{font-family:Inter,sans-serif;font-weight:400;font-size:14px;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65));margin:0}.wcm-close-btn{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;flex-shrink:0;border:none;background:transparent;color:var(--ds-content-low, #00000066);cursor:pointer;padding:0;transition:opacity .15s ease}.wcm-close-btn:hover{opacity:.6}.wcm-close-btn>svg{width:24px;height:24px}.wcm-divider{display:none}.wcm-body{display:flex;flex-direction:column;gap:16px;padding:24px}.wcm-section-header{display:flex;align-items:center;gap:8px;width:100%}.wcm-section-title{font-family:Inter,sans-serif;font-weight:600;font-size:16px;line-height:1;letter-spacing:0;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wcm-recommended-badge{display:inline-flex;align-items:center;justify-content:center;padding:4px 8px;background:var(--ds-accent-primary-low, #fff5ee);border-radius:9999px;font-family:Inter,sans-serif;font-weight:500;font-size:12px;line-height:1;letter-spacing:0;color:var(--ds-accent-primary-high, #f0640d);white-space:nowrap}.wcm-social-grid{display:grid;gap:8px;width:100%}.wcm-dialog-body .wcm-social-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.wcm-drawer-body .wcm-social-grid{grid-template-columns:1fr}.wcm-social-item{display:flex;background:var(--ds-surface-medium, #f5f7fa);border:none;border-radius:12px;cursor:pointer;transition:none;font-family:inherit;color:var(--ds-content-high, rgba(0, 0, 0, .85))}.wcm-social-item:hover{background:#2e3544!important}:root[data-ds-theme=light] .wcm-social-item:hover{background:#eff3f8!important}.wcm-social-item[data-provider=apple] .wcm-social-icon{color:#000}[data-theme=dark] .wcm-social-item[data-provider=apple] .wcm-social-icon{color:#fff}.wcm-dialog-body .wcm-social-item{flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px}.wcm-drawer-body .wcm-social-item{flex-direction:row;align-items:center;justify-content:flex-start;gap:12px;padding:16px}.wcm-social-icon{flex-shrink:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center}.wcm-social-icon>svg{width:32px;height:32px}.wcm-social-name{font-family:Inter,sans-serif;font-weight:600;font-size:14px;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap}.wcm-separator{display:flex;align-items:center;gap:12px;width:100%}.wcm-separator-line{flex:1;height:1px;background:var(--ds-border-default, rgba(0, 0, 0, .07))}.wcm-separator-text{font-family:Inter,sans-serif;font-weight:500;font-size:12px;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-low, rgba(0, 0, 0, .4));white-space:nowrap}.wcm-wallet-list{display:flex;flex-direction:column;gap:8px}.wcm-wallet-item{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;padding:20px 16px;background:var(--ds-surface-medium, #f5f7fa);border:none;border-radius:12px;cursor:pointer;text-align:left;transition:none;font-family:inherit}.wcm-wallet-item:hover{background:#2e3544!important}:root[data-ds-theme=light] .wcm-wallet-item:hover{background:#eff3f8!important}.wcm-wallet-item-left{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.wcm-wallet-icon{flex-shrink:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center}.wcm-wallet-icon>svg,.wcm-wallet-icon>img{width:28px;height:28px;border-radius:0;object-fit:cover}.wcm-wallet-name-row{display:flex;align-items:center;gap:8px;min-width:0}.wcm-wallet-name{font-family:Inter,sans-serif;font-weight:600;font-size:16px;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-high, rgba(0, 0, 0, .85));white-space:nowrap}.wcm-wallet-badge{display:inline-flex;align-items:center;justify-content:center;padding:4px 8px;background:var(--ds-system-purple-low, #f5f3ff);border:.5px solid var(--ds-system-purple-default, #7e54f4);border-radius:9999px;font-family:Inter,sans-serif;font-weight:500;font-size:12px;line-height:1;letter-spacing:0;color:var(--ds-system-purple-high, #7346f3);white-space:nowrap}.wcm-install-btn{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;padding:4px 8px;background:#ff8438;border-radius:6px;font-family:Inter,sans-serif;font-weight:500;font-size:12px;line-height:1;letter-spacing:0;color:#fff;white-space:nowrap}.wcm-installed-label{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;font-family:Inter,sans-serif;font-weight:500;font-size:12px;line-height:1;letter-spacing:0;color:var(--ds-content-low, rgba(0, 0, 0, .4));white-space:nowrap}.wcm-terms{font-family:Inter,sans-serif;font-weight:400;font-size:12px;line-height:1.5;letter-spacing:-.01em;color:var(--ds-content-medium, rgba(0, 0, 0, .65));text-align:center;margin:0}.wcm-terms-link{color:var(--ds-content-high, rgba(0, 0, 0, .85));text-decoration:underline;text-underline-position:from-font}.wcm-terms-link:hover{color:var(--ds-content-high, rgba(0, 0, 0, .85));text-decoration:underline}.wcm-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}\\n\")","import styleInject from '#style-inject';styleInject(\"@keyframes wcm-overlay-show{0%{opacity:0}to{opacity:1}}@keyframes wcm-overlay-hide{0%{opacity:1}to{opacity:0}}@keyframes wcm-dialog-show{0%{opacity:0;transform:translate(-50%,-48%) scale(.96)}to{opacity:1;transform:translate(-50%,-50%) scale(1)}}@keyframes wcm-dialog-hide{0%{opacity:1;transform:translate(-50%,-50%) scale(1)}to{opacity:0;transform:translate(-50%,-48%) scale(.96)}}\\n\")","\"use client\";\n\nimport { WALLET_LABELS } from \"@nexus-cross/crossx-design-system\";\nimport * as React from \"react\";\nimport \"../styles/theme.css\";\nimport \"../styles/component.css\";\nimport { WalletInfo } from \"../../wallet-info/components/WalletInfo\";\nimport type { ConnectButtonProps, WalletProvider } from \"../types\";\nimport {\n  APPLE_ICON,\n  BINANCE_ICON,\n  CROSSX_ICON,\n  GOOGLE_ICON,\n  METAMASK_ICON,\n} from \"../icons\";\nimport {\n  TrackingBoundary,\n  setDappUiAnalyticsUser,\n} from \"../../../lib/sentry/index\";\n\n/**\n * 3-state wallet connect button. Caller drives the visual state:\n *\n *   - `isConnecting === true`        → 스피너 disabled 버튼\n *   - `address` 있음                 → WalletInfo + connected pill\n *   - `address`가 있는데 `provider` 없음 → SDK 조회 전이 상태, placeholder 원\n *   - 둘 다 없음                     → \"Connect Wallet\" 버튼\n *\n * view-only: wagmi / connect-kit 같은 web3 의존성은 갖지 않으며 모든\n * 상태/데이터/콜백은 props로 주입받는다. 실제 wagmi 연결 로직은\n * `@nexus-cross/connect-kit-react`의 상위 래퍼에서 수행한다.\n */\nexport function ConnectButton({\n  isConnecting,\n  address,\n  provider,\n  providerName,\n  accountName,\n  sendAccounts,\n  onConnect,\n  onDisconnect,\n  onCopy,\n  onSelectWallet,\n  onBuy,\n  onBuyDisabledMessage,\n  label = \"Connect Wallet\",\n  connectingLabel = \"Connecting...\",\n  disconnectLabel,\n  className,\n  theme = \"dark\",\n  env,\n  showBalance = true,\n  showPortfolio,\n  drawerDirection,\n  modal,\n  connectorId,\n  style,\n  walletInfoStyle,\n  sendTransaction,\n  getTransactionReceipt,\n  estimateGas,\n  onReceive,\n  onBridge,\n  onSend,\n  bridgeTokens,\n  bridgeHistory,\n  getBridgeQuote,\n  getBridgeToTokens,\n  getBridgeApproval,\n  approveBridge,\n  submitBridge,\n}: ConnectButtonProps) {\n  const buttonClass = className ? `cb-button ${className}` : \"cb-button\";\n  const pillClass = className ? `cb-pill ${className}` : \"cb-pill\";\n\n  React.useEffect(() => {\n    setDappUiAnalyticsUser(address);\n  }, [address]);\n\n  if (isConnecting) {\n    return (\n      <button\n        type=\"button\"\n        disabled\n        className={buttonClass}\n        style={style}\n        aria-busy=\"true\"\n      >\n        <span className=\"cb-spinner\" aria-hidden=\"true\" />\n        <span className=\"cb-button-label\">{connectingLabel}</span>\n      </button>\n    );\n  }\n\n  if (address) {\n    const resolvedProviderName =\n      providerName ?? getDefaultProviderName(provider);\n    const iconUrl = provider ? PROVIDER_ICON[provider] : undefined;\n\n    return (\n      <TrackingBoundary feature=\"connect_button\">\n        <WalletInfo\n          env={env}\n          theme={theme}\n          drawerDirection={drawerDirection}\n          modal={modal}\n          showBalance={showBalance}\n          showPortfolio={showPortfolio}\n          walletAddress={address}\n          accountName={accountName}\n          sendAccounts={sendAccounts}\n          // 계정 라벨이 없을 때 WalletInfo 가 쓰는 폴백. `getDefaultProviderName`\n          // 의 \"Wallet\" placeholder 는 의도적으로 넘기지 않는다 — provider 를\n          // 모르는 상태를 계정 라벨로 승격시키면 안 된다.\n          connectorName={\n            providerName ?? (provider ? PROVIDER_NAME[provider] : undefined)\n          }\n          connectorId={connectorId}\n          onSelectWallet={onSelectWallet}\n          onCopyAddress={\n            onCopy\n              ? (_: string, success: boolean) => {\n                  if (success) onCopy();\n                }\n              : undefined\n          }\n          onDisconnect={onDisconnect}\n          disconnectLabel={disconnectLabel}\n          style={walletInfoStyle}\n          sendTransaction={sendTransaction}\n          getTransactionReceipt={getTransactionReceipt}\n          estimateGas={estimateGas}\n          onReceive={onReceive}\n          onBridge={onBridge}\n          onBuy={onBuy}\n          onBuyDisabledMessage={onBuyDisabledMessage}\n          onSend={onSend}\n          bridgeTokens={bridgeTokens}\n          bridgeHistory={bridgeHistory}\n          getBridgeQuote={getBridgeQuote}\n          getBridgeToTokens={getBridgeToTokens}\n          getBridgeApproval={getBridgeApproval}\n          approveBridge={approveBridge}\n          submitBridge={submitBridge}\n        >\n          <WalletInfo.Trigger asChild>\n            <button\n              type=\"button\"\n              className={pillClass}\n              data-track=\"wallet_pill_open\"\n              style={style}\n              aria-label={`Connected with ${resolvedProviderName}: ${shorten(address)}`}\n            >\n              {iconUrl ? (\n                <img\n                  src={iconUrl}\n                  alt=\"\"\n                  width={18}\n                  height={18}\n                  className=\"cb-pill-icon\"\n                  data-provider={provider}\n                />\n              ) : (\n                <span className=\"cb-pill-icon-placeholder\" aria-hidden=\"true\" />\n              )}\n              <span className=\"cb-pill-address\">{shorten(address)}</span>\n            </button>\n          </WalletInfo.Trigger>\n          <WalletInfo.Content align=\"end\" sideOffset={8} />\n        </WalletInfo>\n      </TrackingBoundary>\n    );\n  }\n\n  return (\n    <TrackingBoundary feature=\"connect_button\">\n      <button\n        type=\"button\"\n        onClick={onConnect}\n        className={buttonClass}\n        data-track=\"connect_click\"\n        style={style}\n        aria-label={typeof label === \"string\" ? label : \"Connect Wallet\"}\n      >\n        <ConnectWalletIcon />\n        <span className=\"cb-button-label\">{label}</span>\n      </button>\n    </TrackingBoundary>\n  );\n}\n\nfunction ConnectWalletIcon() {\n  return (\n    <svg\n      className=\"cb-button-icon\"\n      viewBox=\"0 0 18 18\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M3.75 5.25H14.25C15.0784 5.25 15.75 5.92157 15.75 6.75V13.5C15.75 14.3284 15.0784 15 14.25 15H3.75C2.92157 15 2.25 14.3284 2.25 13.5V4.5C2.25 3.67157 2.92157 3 3.75 3H13.5\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.35\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M12 9.75H15.75V12.75H12C11.1716 12.75 10.5 12.0784 10.5 11.25C10.5 10.4216 11.1716 9.75 12 9.75Z\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.35\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      <path\n        d=\"M12.75 11.25H12.7575\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  );\n}\n\nconst PROVIDER_ICON: Record<WalletProvider, string> = {\n  google: GOOGLE_ICON,\n  apple: APPLE_ICON,\n  cross: CROSSX_ICON,\n  metamask: METAMASK_ICON,\n  binance: BINANCE_ICON,\n};\n\nconst PROVIDER_NAME: Record<WalletProvider, string> = {\n  google: \"Google\",\n  apple: \"Apple\",\n  cross: WALLET_LABELS.default,\n  metamask: \"MetaMask\",\n  binance: \"Binance Wallet\",\n};\n\nfunction getDefaultProviderName(provider: WalletProvider | undefined): string {\n  if (!provider) return \"Wallet\";\n  return PROVIDER_NAME[provider];\n}\n\nfunction shorten(address: string): string {\n  if (address.length <= 10) return address;\n  return `${address.slice(0, 6)}...${address.slice(-4)}`;\n}\n","import styleInject from '#style-inject';styleInject(\".cb-button{--cb-bg: var(--ds-accent-gradient, linear-gradient(196deg, #ff2589 16%, #ff823d 84%));--cb-bg-hover: var(--cb-bg);--cb-color: #ffffff;--cb-border: none;--cb-radius: 8px;--cb-height: 36px;--cb-padding: 8px 16px;--cb-font-family: Inter, system-ui, -apple-system, sans-serif;--cb-font-size: 14px;--cb-font-weight: 500;--cb-line-height: 1.5;--cb-letter-spacing: -.01em;--cb-gap: 8px;--cb-transition: opacity .15s;--cb-loading-opacity: .7;--cb-press-scale: .95;--cb-icon-size: 18px;--cb-spinner-size: 18px;--cb-spinner-thumb: currentColor;--cb-spinner-track: color-mix(in srgb, currentColor 20%, transparent)}.cb-pill{--cb-pill-bg: var(--ds-surface-default, #ffffff);--cb-pill-bg-hover: var(--cb-pill-bg);--cb-pill-color: var(--ds-content-high, rgba(0, 0, 0, .85));--cb-pill-border: 1px solid var(--ds-border-default, rgba(0, 0, 0, .07));--cb-pill-border-hover: 1px solid var(--ds-border-medium, rgba(0, 0, 0, .18));--cb-pill-radius: 8px;--cb-pill-height: 36px;--cb-pill-padding: 1px 13px;--cb-pill-press-scale: .95;--cb-pill-font-family: Inter, system-ui, -apple-system, sans-serif;--cb-pill-font-size: 14px;--cb-pill-font-weight: 500;--cb-pill-line-height: 1.5;--cb-pill-gap: 8px;--cb-pill-icon-size: 18px;--cb-pill-icon-placeholder-bg: var(--ds-surface-medium, #f5f7fa);--cb-pill-address-font: Inter, system-ui, -apple-system, sans-serif;--cb-pill-address-font-size: 14px;--cb-pill-address-letter-spacing: -.01em}\\n\")","import styleInject from '#style-inject';styleInject(\".cb-button{-webkit-font-smoothing:antialiased;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:var(--cb-height);gap:var(--cb-gap);padding:var(--cb-padding);border-radius:var(--cb-radius);border:var(--cb-border);background:var(--cb-bg);color:var(--cb-color);font-family:var(--cb-font-family);font-size:var(--cb-font-size);font-weight:var(--cb-font-weight);line-height:var(--cb-line-height);letter-spacing:var(--cb-letter-spacing);cursor:pointer;transition:var(--cb-transition)}.cb-button:hover:not(:disabled){background:var(--cb-bg-hover)}.cb-button:active:not(:disabled){transform:scale(var(--cb-press-scale))}.cb-button:disabled,.cb-button[aria-busy=true]{cursor:default;opacity:var(--cb-loading-opacity)}.cb-spinner{display:inline-block;width:var(--cb-spinner-size);height:var(--cb-spinner-size);border:2px solid var(--cb-spinner-track);border-top-color:var(--cb-spinner-thumb);border-radius:50%;animation:cb-spin .6s linear infinite;flex-shrink:0}.cb-button-icon{width:var(--cb-icon-size);height:var(--cb-icon-size);flex-shrink:0}.cb-button-label{white-space:nowrap}.cb-pill{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:var(--cb-pill-height);gap:var(--cb-pill-gap);padding:var(--cb-pill-padding);border-radius:var(--cb-pill-radius);border:var(--cb-pill-border);background:var(--cb-pill-bg);color:var(--cb-pill-color);font-family:var(--cb-pill-font-family);font-size:var(--cb-pill-font-size);font-weight:var(--cb-pill-font-weight);line-height:var(--cb-pill-line-height);cursor:pointer}.cb-pill:hover{background:var(--cb-pill-bg-hover)}.cb-pill:active:not(:disabled){transform:scale(var(--cb-pill-press-scale))}.cb-pill-icon{width:var(--cb-pill-icon-size);height:var(--cb-pill-icon-size);border-radius:50%;object-fit:contain;flex-shrink:0;background:transparent}[data-theme=dark] .cb-pill-icon[data-provider=apple]{filter:invert(1)}.cb-pill-icon-placeholder{display:inline-block;width:var(--cb-pill-icon-size);height:var(--cb-pill-icon-size);border-radius:50%;background:var(--cb-pill-icon-placeholder-bg);flex-shrink:0}.cb-pill-address{font-family:var(--cb-pill-address-font);font-size:var(--cb-pill-address-font-size);letter-spacing:var(--cb-pill-address-letter-spacing);white-space:nowrap}@media(max-width:767.98px){.cb-button,.cb-pill{width:36px;min-width:36px;padding:0}.cb-button{height:36px}.cb-button-label,.cb-pill-address{display:none}}@keyframes cb-spin{to{transform:rotate(360deg)}}\\n\")","/**\n * Wallet provider icons used by `ConnectButton`. Ported from\n * `@nexus-cross/connect-kit-wagmi` so dapp-ui stays a self-contained,\n * bundler-agnostic module (no SVG loader required) and doesn't pull in a\n * web3 dependency for icon assets.\n *\n * Each icon is an inline `data:image/svg+xml` URI so it can be consumed\n * directly by `<img src={...}>`.\n *\n * Source references mirror the ones in `connect-kit-wagmi/src/wallets/icons.ts`:\n *   wallet-crossx.svg        → CROSSX_ICON        (cross / cross-extension / cross-embedded fallback)\n *   wallet-metamask.svg      → METAMASK_ICON\n *   wallet-binance.svg       → BINANCE_ICON\n */\n\nconst CROSSX_SVG = `<svg preserveAspectRatio=\"xMidYMid meet\" width=\"100%\" height=\"100%\" overflow=\"visible\" style=\"display: block;\" viewBox=\"0 0 100 100\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_72837_24069)\"><mask id=\"mask0_72837_24069\" style=\"mask-type:luminance\" maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width=\"100\" height=\"100\"><path d=\"M50 0C10.5289 0 0 10.5289 0 50C0 89.4711 10.5289 100 50 100C89.4711 100 100 89.4711 100 50C100 10.5289 89.4711 0 50 0Z\" fill=\"white\"/></mask><g mask=\"url(#mask0_72837_24069)\"><path d=\"M99.9501 0.0499268H0V100H99.9501V0.0499268Z\" fill=\"url(#paint0_linear_72837_24069)\"/><path d=\"M34.9302 100H99.9751V19.0619L34.9302 60.7286V100Z\" fill=\"url(#paint1_linear_72837_24069)\"/><path d=\"M34.9301 100H0V23.8024L34.9301 60.7285V100Z\" fill=\"url(#paint2_linear_72837_24069)\"/></g><g filter=\"url(#filter0_d_72837_24069)\"><path d=\"M49.9503 21.4821L31.9862 31.8613C28.8176 33.6827 27.7198 37.7745 29.5411 40.9432C31.3625 44.1118 35.4543 45.2096 38.623 43.3883L53.8176 34.6058L46.582 42.3653V72.8044C46.582 76.4721 49.576 79.4661 53.2437 79.4661C56.9114 79.4661 59.9054 76.4721 59.9054 72.8044V27.2455C59.9054 25.9731 59.5062 24.7256 59.0072 23.9022C57.1359 20.7585 53.094 19.6358 49.9254 21.4571L49.9503 21.4821Z\" fill=\"white\"/></g></g><defs><filter id=\"filter0_d_72837_24069\" x=\"7.4286\" y=\"-0.650014\" width=\"87.831\" height=\"115.47\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\"><feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/><feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\" result=\"hardAlpha\"/><feOffset dx=\"7.06088\" dy=\"7.06088\"/><feGaussianBlur stdDeviation=\"14.1467\"/><feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.831373 0 0 0 0 0.113725 0 0 0 0 0.356863 0 0 0 0.3 0\"/><feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow_72837_24069\"/><feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect1_dropShadow_72837_24069\" result=\"shape\"/></filter><linearGradient id=\"paint0_linear_72837_24069\" x1=\"-11.5768\" y1=\"-12.001\" x2=\"106.562\" y2=\"106.986\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"#F3752D\"/><stop offset=\"1\" stop-color=\"#EC1272\"/></linearGradient><linearGradient id=\"paint1_linear_72837_24069\" x1=\"24.9501\" y1=\"23.004\" x2=\"73.6278\" y2=\"64.8453\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"white\" stop-opacity=\"0.5\"/><stop offset=\"1\" stop-color=\"white\" stop-opacity=\"0\"/></linearGradient><linearGradient id=\"paint2_linear_72837_24069\" x1=\"62.3503\" y1=\"3.46804\" x2=\"16.3423\" y2=\"63.3732\" gradientUnits=\"userSpaceOnUse\"><stop stop-color=\"white\" stop-opacity=\"0.5\"/><stop offset=\"1\" stop-color=\"white\" stop-opacity=\"0\"/></linearGradient><clipPath id=\"clip0_72837_24069\"><rect width=\"100\" height=\"100\" fill=\"white\"/></clipPath></defs></svg>`;\n\nconst METAMASK_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100%\" height=\"100%\" viewBox=\"0 0 28 28\" fill=\"none\"><g clip-path=\"url(#m0)\"><path d=\"M26.07 26.93L20.06 25.07L15.53 27.88L12.37 27.88L7.83 25.07L1.83 26.93L0 20.53L1.83 13.44L0 7.44L1.83 0L11.21 5.81H16.69L26.07 0L27.9 7.44L26.07 13.44L27.9 20.53Z\" fill=\"#FF5C16\"/><path d=\"M7.83 20.54L11.96 23.8L7.83 25.07V20.54Z\" fill=\"#FF5C16\"/><path d=\"M20.06 20.54L15.93 23.8L20.06 25.07L20.06 20.54Z\" fill=\"#FF5C16\"/><path d=\"M11.63 15.14L12.78 22.85L11.19 18.56L5.77 17.17L7.83 15.14H11.63Z\" fill=\"#E34807\"/><path d=\"M16.27 15.14L15.12 22.85L16.71 18.56L22.13 17.17L20.07 15.14H16.27Z\" fill=\"#E34807\"/><path d=\"M0 20.53L1.83 13.44H5.76L5.77 17.17L11.19 18.56L12.78 22.85L11.96 23.79L7.83 20.53H0Z\" fill=\"#FF8D5D\"/><path d=\"M27.9 20.53L26.07 13.44H22.14L22.13 17.17L16.71 18.56L15.12 22.85L15.94 23.79L20.07 20.53H27.9Z\" fill=\"#FF8D5D\"/><path d=\"M16.69 5.81H11.21L10.84 9.81L12.78 22.85H15.12L17.06 9.81L16.69 5.81Z\" fill=\"#FF8D5D\"/><path d=\"M15.53 25.34V27.88H12.37V25.34H15.53Z\" fill=\"#C0C4CD\"/><path d=\"M7.83 25.07L12.37 27.88V25.34L11.96 23.79L7.83 25.07Z\" fill=\"#E7EBF6\"/><path d=\"M20.07 25.07L15.53 27.88V25.34L15.94 23.79L20.07 25.07Z\" fill=\"#E7EBF6\"/></g><defs><clipPath id=\"m0\"><rect width=\"28\" height=\"28\" fill=\"#fff\"/></clipPath></defs></svg>`;\n\nconst BINANCE_SVG = `<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 40 40\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.23 16.81L20 9.04L27.77 16.81L32.29 12.29L20 0L7.71 12.29L12.23 16.81Z\" fill=\"#F3BA2F\"/><path d=\"M9.04 20L4.52 15.48L0 20L4.52 24.52L9.04 20Z\" fill=\"#F3BA2F\"/><path d=\"M12.23 23.19L20 30.96L27.77 23.19L32.29 27.71L20 40L7.71 27.71L12.23 23.19Z\" fill=\"#F3BA2F\"/><path d=\"M35.48 24.52L40 20L35.48 15.48L30.96 20L35.48 24.52Z\" fill=\"#F3BA2F\"/><path d=\"M24.58 20L20 15.41L16.61 18.8L16.22 19.19L15.42 19.99L20 24.59L24.59 20L24.58 20Z\" fill=\"#F3BA2F\"/></svg>`;\n\n// Google \"G\" — four-color official logo. Used when the user signed into\n// crossy-sdk 2.0 via Google OAuth.\nconst GOOGLE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 48 48\"><path fill=\"#FFC107\" d=\"M43.6 20.5H42V20H24v8h11.3A12 12 0 1 1 24 12c3 0 5.8 1.2 7.9 3l5.7-5.7A20 20 0 1 0 44 24c0-1.2-.1-2.4-.4-3.5z\"/><path fill=\"#FF3D00\" d=\"M6.3 14.7l6.6 4.8A12 12 0 0 1 24 12c3 0 5.8 1.2 7.9 3l5.7-5.7A20 20 0 0 0 6.3 14.7z\"/><path fill=\"#4CAF50\" d=\"M24 44c5.2 0 10-2 13.6-5.2l-6.3-5.3A12 12 0 0 1 12.7 28l-6.5 5A20 20 0 0 0 24 44z\"/><path fill=\"#1976D2\" d=\"M43.6 20.5H42V20H24v8h11.3a12 12 0 0 1-4.1 5.5l6.3 5.3C41 35.7 44 30.3 44 24c0-1.2-.1-2.4-.4-3.5z\"/></svg>`;\n\n// Apple logomark — solid black. Rendered via `<img src=data:…>`, where\n// `currentColor` can't inherit the page color (img SVGs have no CSS\n// context), so we bake an explicit black fill. The connect-button pill\n// flips it to white in dark mode via `filter: invert(1)` on\n// `.cb-pill-icon[data-provider=\"apple\"]` (see component.css).\nconst APPLE_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"#000\"><path d=\"M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.53 4.08zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z\"/></svg>`;\n\nfunction toDataUri(svg: string): string {\n  return `data:image/svg+xml,${encodeURIComponent(svg)}`;\n}\n\nexport const CROSSX_ICON = toDataUri(CROSSX_SVG);\nexport const METAMASK_ICON = toDataUri(METAMASK_SVG);\nexport const BINANCE_ICON = toDataUri(BINANCE_SVG);\nexport const GOOGLE_ICON = toDataUri(GOOGLE_SVG);\nexport const APPLE_ICON = toDataUri(APPLE_SVG);\n","\"use client\";\n\nimport * as React from \"react\";\nimport \"../styles/theme.css\";\nimport \"../styles/component.css\";\nimport type { SkillsButtonProps } from \"../types\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\nimport {\n  resolveEnvValue,\n  useRemoteConfigValue,\n  type AppsConfig,\n} from \"../../../lib/remote-config\";\nimport { TrackingBoundary } from \"../../../lib/sentry/index\";\n\n/**\n * @deprecated 이동 대상은 apps.json(skills)이 단일 소스다. 이 상수는 더 이상\n * 폴백으로 쓰이지 않으며 하위 호환(공개 export)용으로만 남아 있다.\n */\nexport const DEFAULT_SKILLS_HREF = \"https://www.onechain.nexus/skills\";\n\nexport function SkillsButton({\n  label = \"Skills\",\n  href,\n  onClick,\n  className,\n  style,\n  theme,\n  disabled = false,\n  isLoading = false,\n  loadingLabel = \"Loading...\",\n  openInNewTab = false,\n  type = \"button\",\n}: SkillsButtonProps) {\n  const [loading, setLoading] = React.useState(false);\n\n  const env = resolveEnvironment();\n  const remoteHref = useRemoteConfigValue<AppsConfig, string>(\n    \"apps.json\",\n    env,\n    (config) => resolveEnvValue(config?.skills, env),\n  );\n  // 우선순위: 명시 href > apps.json 원격 값. 원격 로드 전 클릭은 no-op.\n  const resolvedHref = href ?? remoteHref;\n\n  const handleClick = async (_e: React.MouseEvent<HTMLButtonElement>) => {\n    if (resolvedHref && !onClick) {\n      if (openInNewTab) {\n        window.open(resolvedHref, \"_blank\", \"noopener,noreferrer\");\n      } else {\n        window.location.href = resolvedHref;\n      }\n      return;\n    }\n\n    if (onClick) {\n      try {\n        setLoading(true);\n        await onClick();\n      } finally {\n        setLoading(false);\n      }\n    }\n  };\n\n  const isDisabled = disabled || isLoading || loading;\n  const showLoading = isLoading || loading;\n  const displayLabel = showLoading ? loadingLabel : label;\n  const classes = className ? `sb-button ${className}` : \"sb-button\";\n\n  return (\n    <TrackingBoundary feature=\"skills\">\n      <button\n        type={type}\n        onClick={handleClick}\n        disabled={isDisabled}\n        className={classes}\n        data-theme={theme}\n        data-track=\"click\"\n        style={style}\n        aria-busy={showLoading}\n      >\n        {showLoading ? (\n          <span className=\"sb-spinner\" aria-hidden=\"true\" />\n        ) : (\n          <SparklesIcon />\n        )}\n        <span className=\"sb-label\">{displayLabel}</span>\n      </button>\n    </TrackingBoundary>\n  );\n}\n\nfunction SparklesIcon() {\n  return (\n    <svg\n      className=\"sb-button-icon\"\n      viewBox=\"0 0 34 34\"\n      fill=\"none\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"M15.5879 3.95142C16.0211 2.78097 17.6761 2.78099 18.1094 3.95142L19.6221 8.03833C20.6569 10.8348 22.8618 13.0396 25.6582 14.0745L29.7451 15.5872C30.9157 16.0204 30.9157 17.6754 29.7451 18.1086L25.6582 19.6213C22.8618 20.6562 20.657 22.861 19.6221 25.6575L18.1094 29.7444C17.6762 30.9149 16.0211 30.9149 15.5879 29.7444L14.0752 25.6575C13.0403 22.861 10.8355 20.6562 8.03906 19.6213L3.95215 18.1086C2.78172 17.6754 2.7817 16.0204 3.95215 15.5872L8.03906 14.0745C10.8355 13.0396 13.0403 10.8348 14.0752 8.03833L15.5879 3.95142Z\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.93556\"\n      />\n    </svg>\n  );\n}\n","import styleInject from '#style-inject';styleInject(\".sb-button{--sb-bg: var(--ds-surface-default, #ffffff);--sb-border: var(--ds-border-default, rgba(0, 0, 0, .07));--sb-border-hover: var(--ds-border-default, rgba(0, 0, 0, .07));--sb-color: var(--ds-content-medium, rgba(0, 0, 0, .65));--sb-color-hover: var(--ds-content-high, rgba(0, 0, 0, .85));--sb-radius: 8px;--sb-height: 36px;--sb-padding: 1px 11px;--sb-gap: 4px;--sb-font-size: 14px;--sb-font-weight: 500;--sb-line-height: 21px;--sb-letter-spacing: 0;--sb-blur: 0px;--sb-border-width: 1px;--sb-icon-size: 18px;--sb-icon-hover-rotation: 180deg;--sb-spinner-size: 18px;--sb-spinner-thumb: currentColor;--sb-spinner-track: color-mix(in srgb, currentColor 20%, transparent);--sb-disabled-opacity: .6;--sb-press-scale: .95;--sb-hover-duration: .2s;--sb-icon-duration: .3s;--sb-easing: ease}.sb-button[data-theme=dark]{--sb-bg: var(--ds-surface-default, #1e232e);--sb-border: var(--ds-border-default, #ffffff12);--sb-border-hover: var(--ds-border-medium, #ffffff2e);--sb-color: var(--ds-content-medium, #ffffffa6);--sb-color-hover: var(--ds-content-high, #ffffffd9)}.sb-button[data-theme=light]{--sb-bg: var(--ds-surface-default, #ffffff);--sb-border: var(--ds-border-default, #00000012);--sb-border-hover: var(--ds-border-medium, #0000002e);--sb-color: var(--ds-content-medium, #000000a6);--sb-color-hover: var(--ds-content-high, #000000d9)}\\n\")","import styleInject from '#style-inject';styleInject(\".sb-button{-webkit-font-smoothing:antialiased;display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;height:var(--sb-height);gap:var(--sb-gap);padding:var(--sb-padding);border-radius:var(--sb-radius);border:var(--sb-border-width) solid var(--sb-border);background:var(--sb-bg);backdrop-filter:blur(var(--sb-blur));-webkit-backdrop-filter:blur(var(--sb-blur));color:var(--sb-color);font-family:Inter,system-ui,-apple-system,sans-serif;font-weight:var(--sb-font-weight);font-size:var(--sb-font-size);line-height:var(--sb-line-height);letter-spacing:var(--sb-letter-spacing);cursor:pointer;transition:border-color var(--sb-hover-duration) var(--sb-easing),color var(--sb-hover-duration) var(--sb-easing)}@media(hover:hover){.sb-button:hover:not(:disabled){border-color:var(--sb-border-hover);color:var(--sb-color-hover)}.sb-button:hover:not(:disabled) .sb-button-icon{transform:rotate(var(--sb-icon-hover-rotation))}}.sb-button:active:not(:disabled){transform:scale(var(--sb-press-scale))}.sb-button:disabled,.sb-button[aria-busy=true]{cursor:default;opacity:var(--sb-disabled-opacity)}.sb-button-icon{width:var(--sb-icon-size);height:var(--sb-icon-size);flex-shrink:0;color:inherit;transition:transform var(--sb-icon-duration) var(--sb-easing),color var(--sb-icon-duration) var(--sb-easing)}.sb-spinner{display:inline-block;width:var(--sb-spinner-size);height:var(--sb-spinner-size);border:2px solid var(--sb-spinner-track);border-top-color:var(--sb-spinner-thumb);border-radius:50%;animation:sb-spin .6s linear infinite;flex-shrink:0}.sb-label{white-space:nowrap}@keyframes sb-spin{to{transform:rotate(360deg)}}\\n\")"],"mappings":"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A,cAAAC,GACA,eAAAC,GACA,kBAAAC,GACA,gBAAAC,GACA,iBAAAC,GACA,gBAAAC,GACA,sBAAAC,GACA,YAAAC,GACA,UAAAC,GACA,gBAAAC,GACA,qBAAAC,GACA,aAAAC,GACA,cAAAC,GACA,eAAAC,GACA,kBAAAC,GACA,kBAAAC,GACA,cAAAC,GACA,aAAAC,GACA,UAAAC,GACA,SAAAC,GACA,OAAAC,EACF,GACA,CACEG,GACAnE,EACAyE,GACAQ,GACAN,GACAE,GACAzE,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACA0D,GACAC,GACAvD,EACAmE,GACAlE,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACA2E,EACA1E,EACAC,EACAE,EACAC,EACAE,EACAkC,GACAqB,GACAE,GACAE,GACAE,GACAE,GACAE,GACA9D,GACAC,EACAC,EACAC,EACAC,GACAC,GACAC,EACAC,GACAC,EACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,EACF,CACF,EAEA,SACE,QAACsC,GAAkB,SAAlB,CAA2B,MAAOD,GAChC,SAAA5B,MACC,QAAC,UAAO,KAAP,CAEC,UAAWE,GACX,KAAMM,GACN,aAAcC,EACd,WAAYN,GACZ,aAAY,GACZ,iBAAkB,GAEjB,SAAAV,IARG,QASN,KAEA,QAAS,QAAR,CAA2B,KAAMe,GAAM,aAAcC,EACnD,SAAAhB,IADe,SAElB,EAEJ,CAEJ,CAEO,IAAMqC,GAAa,OAAO,OAAOzG,GAAgB,CACtD,QAAS0G,GACT,QAASC,GACT,IAAKC,GACL,OAAQC,EACV,CAAC,EuGljBD,IAAAC,GAAuB,sBCmBvB,IAAAC,GAAuB,sBCnBvB,IAAAC,GAA0C,iBAiB7BC,MAAsB,kBAAwC,IAAI,EAExE,SAASC,IAA4C,CAC1D,IAAMC,KAAM,eAAWF,EAAmB,EAC1C,GAAI,CAACE,EACH,MAAM,IAAI,MACR,qEACF,EAE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","names":["index_exports","__export","APPLE_ICON","AppLauncher","AppLauncherContent","AppLauncherTrigger","BINANCE_ICON","BridgeFlow","CHAINS_CONFIG_FILE","CONNECTOR_REGISTRY","CROSSX_ICON","ConnectButton","ConnectorId","DEFAULT_SKILLS_HREF","DEFAULT_TRUSTED_FACTORIES","DappUiErrorBoundary","GOOGLE_ICON","GetOneUsd","METAMASK_ICON","OnePopBody","OnePopGetTokenTrigger","PORTFOLIO_SECTIONS","RelayApiError","RelayDeposit","RelayHistory","RelayRecovery","SOCIAL_REGISTRY","SendFlow","SkillsButton","StatusTracker","TOKEN_STATS_QUERY_KEY","USER_BALANCE_QUERY_KEY","WALLET_PORTFOLIO_QUERY_PREFIX","WALLET_REGISTRY","WalletConnectModal","WalletInfo","WalletPortfolio","WalletPortfolioBody","announceAppLauncherUsage","captureDappUiException","createRelayClient","findGetOneUsdToken","getChainDisplay","getDappUiSentryScope","getOneUsdTokenKey","initDappUiSentry","invalidateWalletPortfolioQueries","isGetOneUsdActionEnabled","isGetOneUsdActionSupported","isGetOneUsdActionVisible","isGetOneUsdTargetAvailable","matchesGetOneUsdToken","normalizeFailureReason","resolveEnvironment","resolveGetOneUsdRoute","setDappUiAnalyticsUser","trackDappUiEvent","trackDappUiFunnel","useChainDisplay","useChainsConfig","useGlobalMenu","useRelayConfig","useRelayOrders","useRelayRecovery","useTokenBalance","useTokenStats","useWalletDetect","__toCommonJS","import_meta","readViteEnv","key","readNodeEnv","resolveEnvironment","env","normalizeEnvironment","crossxEnv","value","React","REMOTE_CONFIG_BASES","FETCH_TIMEOUT_MS","resolveEnvValue","value","env","configCache","fetchRemoteConfig","file","url","cached","controller","timer","res","result","isSafeBaseUrl","value","getApiBase","name","env","config","fetchRemoteConfig","remote","resolveEnvValue","resolveRelayForwarderFactories","labelled","labelledAddresses","CHAINS_CONFIG_FILE","getChainDisplay","chainId","useChainDisplay","meta","setMeta","alive","useChainsConfig","chains","setChains","useRemoteConfigValue","file","select","setValue","selectRef","next","DAPP_UI_VERSION","GLOBAL_KEY","getState","global","scheduleFlush","state","flush","kitVersion","DAPP_UI_VERSION","announceAppLauncherUsage","options","import_react","DAPP_UI_SENTRY_DSN","DAPP_UI_SENTRY_RELEASE","DAPP_UI_VERSION","dappUiScope","analyticsSampleRate","getAnalyticsSampleRate","toSentryEnvironment","environment","initDappUiSentry","options","resolveEnvironment","client","scope","getDappUiSentryScope","captureDappUiException","error","extra","import_react","DappUiErrorBoundary","error","info","captureDappUiException","cn","classes","WALLET_UA_PATTERN","isInAppBrowser","STORAGE_KEY","memoryAnonId","randomUuid","getDappUiAnonId","stored","hashWalletAddress","address","bytes","digest","b","detectDeviceType","ua","commonTags","isInAppBrowser","userInitialized","ensureAnalyticsScope","scope","getDappUiSentryScope","initDappUiSentry","getDappUiAnonId","sampledOut","getAnalyticsSampleRate","userUpdateSeq","setDappUiAnalyticsUser","address","seq","hashWalletAddress","walletId","trackDappUiEvent","feature","action","tags","trackDappUiFunnel","flow","step","options","status","normalizeFailureReason","error","err","message","import_react","import_jsx_runtime","TrackingBoundary","feature","children","handleClick","event","nativeEvent","tracked","action","itemId","trackDappUiEvent","React","Popover","import_vaul","import_react","AppLauncherContext","useAppLauncherContext","ctx","React","MARKER","stack","ourEntries","pendingSelfBack","listening","reconcileScheduled","handlePopState","stopListeningIfIdle","ensureListening","scheduleReconcile","surplus","useHistoryDismiss","open","close","closeRef","pushOverlay","entry","index","import_react","useMediaQuery","query","matches","setMatches","mql","handler","e","styleInject","css","insertAt","head","style","styleInject","styleInject","styleInject","styleInject","styleInject","import_jsx_runtime","AppLauncher","env","theme","mobileBreakpoint","domain","children","isMobile","useMediaQuery","open","setOpen","close","resolvedEnv","resolveEnvironment","useHistoryDismiss","contextValue","AppLauncherContext","Popover","import_vaul","import_jsx_runtime","DotsGridIcon","size","BackArrowIcon","CloseIcon","import_jsx_runtime","AppLauncherTrigger","asChild","children","className","style","isMobile","theme","useAppLauncherContext","hasChildren","shouldAsChild","classes","triggerContent","DotsGridIcon","TrackingBoundary","React","Popover","import_vaul","import_react_query","GLOBAL_MENU_URLS","STAGE_ICON_BASE","PROD_ICON_BASE","icon","env","name","getFallbackGlobalMenu","iconEnv","useGlobalMenu","env","resolvedEnv","resolveEnvironment","res","GLOBAL_MENU_URLS","getFallbackGlobalMenu","getGlobalMenuItemStatus","item","resolveGlobalMenuItemAssetUrl","assetUrl","env","getSortedGlobalMenuItems","items","a","b","statusDelta","getAllMenuSortRank","getAllMenuSortRank","item","status","getGlobalMenuItemStatus","import_jsx_runtime","isStakingItem","item","AppLauncherContent","align","sideOffset","className","env","theme","isMobile","domain","close","useAppLauncherContext","data","useGlobalMenu","menuItems","getSortedGlobalMenuItems","DRAG_THRESHOLD","pointerStart","wasDrag","handlePointerDown","e","handlePointerUp","start","handlePointerCancel","currentPathname","handleItemClick","dragged","url","isExternal","isExternalLink","target","opened","renderMenuItem","serviceStatus","getGlobalMenuItemStatus","isEndingSoon","iconUrl","resolveGlobalMenuItemAssetUrl","isActive","urlPathname","menuContent","TrackingBoundary","cn","BackArrowIcon","CloseIcon","linkHostname","React","Popover","import_vaul","import_react","WalletInfoContext","useWalletInfoContext","ctx","import_crossx_design_system","ConnectorId","CONNECTOR_REGISTRY","React","import_jsx_runtime","WalletIcon","size","className","CopyIcon","CheckIcon","CloseIcon","DisconnectIcon","QRIcon","BackIcon","ChevronDownIcon","size","className","EyeOpenIcon","EyeClosedIcon","QR_LOGO_SVG","QR_LOGO_ICON","QRLogoIcon","size","className","SendActionIcon","SendSolidIcon","PortfolioActionIcon","BuyActionIcon","BridgeActionIcon","ToastCheckIcon","React","import_bignumber","stripTrailingZerosAndDot","s","end","formatWei","value","decimals","bn","BigNumber","dp","shortenAddress","address","formatPrice","value","num","decimals","factor","formatTokenValue","rawQuantity","tokenDecimals","price","BigNumber","formatTokenValueForDisplay","val","raw","newValue","stripTrailingZerosAndDot","integerPart","fractionPart","frac2","import_react","import_react_query","CHAIN_IDS","DEFAULT_TERMS_URL","DEFAULT_PRIVACY_URL","DEFAULT_ONEUSD_TERMS_URL","CHAIN_INFO_QUERY_KEY","getChainInfoPayload","json","payload","first","joinExplorerPath","base","segments","normalizedBase","normalizedSegments","segment","buildTransactionExplorerUrl","env","chainId","chain","txHash","CHAIN_IDS","getCrossTransactionExplorerUrl","getFallbackTransactionExplorerUrl","chains","useTransactionExplorerUrl","enabled","query","signal","baseUrl","getApiBase","res","chainInfo","useExplorerUrlBuilder","NATIVE_TOKEN_SENTINEL_ADDRESS","ERC20_TRANSFER_SELECTOR","ADDRESS_RE","RECENT_SEND_KEY_PREFIX","MAX_RECENT_SEND_ADDRESSES","trimTrailingZeros","value","end","normalizeAddress","address","isAddress","isNativeSendAsset","token","NATIVE_TOKEN_SENTINEL_ADDRESS","getRecentSendStorageKey","walletAddress","getRecentSendAddresses","raw","parsed","item","saveRecentSendAddress","recipient","normalized","next","clearRecentSendAddresses","parseTokenAmount","input","decimals","trimmed","wholePart","fractionPart","whole","paddedFraction","fraction","unit","formatRawTokenAmount","fractionText","encodeErc20Transfer","amount","recipientWord","amountWord","formatTokenUnits","value","decimals","maxDp","unit","whole","fraction","trimmed","formatWeiToNative","wei","formatWeiToGwei","formatGasLimit","gasLimit","isBscLegacyChainId","chainId","getGasPriceWei","estimate","getGasMode","forceLegacy","getEstTxFeeWei","gasPrice","formatUsd","value","collectErrorText","err","depth","e","parts","rawMessage","formatSendError","context","fallback","haystack","nativeSymbol","networkName","where","LEGACY_GAS_PRICE_FALLBACK","DYNAMIC_PRIORITY_FALLBACK","rpcCall","rpcUrl","method","params","controller","timeout","res","json","hexToBigInt","hex","bigIntToHex","value","buildTxParam","args","tx","isLegacyOnlyChain","chainId","fetchLegacyFees","LEGACY_GAS_PRICE_FALLBACK","fetchDynamicFees","baseFee","priority","DYNAMIC_PRIORITY_FALLBACK","fetchFees","legacyOnly","baseFeeHex","fallbackEstimateGas","txParam","gasLimitHex","fees","createSendRepository","deps","estimateGas","sendTransaction","getTransactionReceipt","rpcFallbackUrl","args","fallbackEstimateGas","RECEIPT_POLL_INTERVAL_MS","RECEIPT_POLL_MAX_ATTEMPTS","hasFieldErrors","errors","validateSend","recipient","amount","token","balanceRaw","trimmedRecipient","parsed","parseTokenAmount","rawAmount","isAddress","isNativeSendAsset","buildSendTxArgs","walletAddress","from","normalizeAddress","encodeErc20Transfer","isReceiptReverted","receipt","status","delay","ms","resolve","executeSend","repository","args","chainId","onSubmitted","pollIntervalMs","pollMaxAttempts","hash","getTransactionReceipt","attempt","STEP_ORDER","useSendFlow","options","env","walletAddress","accountName","accounts","initialStep","initialToken","tokens","onTokenChange","sendTransaction","getTransactionReceipt","estimateGas","getTokenPriceUsd","onSubmitted","onSuccess","onFailed","onExit","onOutlink","repository","createSendRepository","token","setToken","step","setStep","recipient","setRecipientState","amount","setAmountState","fieldErrors","setFieldErrors","status","setStatus","txHash","setTxHash","error","setError","gasEstimate","setGasEstimate","gasLoading","setGasLoading","gasError","setGasError","gasReqIdRef","reviewRawAmount","setReviewRawAmount","reviewRecipient","setReviewRecipient","recentAddresses","setRecentAddresses","getRecentSendAddresses","funnelFiredRef","trackFunnelOnce","funnelStep","funnelOptions","trackDappUiFunnel","resolvedEnv","resolveEnvironment","transactionUrl","chainInfo","useTransactionExplorerUrl","chainDisplay","useChainDisplay","nativeSymbol","networkName","errorCtxRef","balanceRaw","balanceText","formatWei","priceUsd","amountUsd","n","setRecipient","value","prev","setAmount","setMax","selectToken","next","goToTokenSelect","back","current","idx","submitRecipient","errors","validateSend","selectRecipient","address","submitAmount","result","hasFieldErrors","reqId","args","buildSendTxArgs","estimate","err","formatSendError","insufficientGas","isNativeSendAsset","feeWei","getEstTxFeeWei","isBscLegacyChainId","confirmSend","submittedHash","outcome","executeSend","hash","saveRecentSendAddress","message","normalizeFailureReason","hashCopied","setHashCopied","copyTimerRef","copyHash","openTransaction","openTab","target","maybe","win","retry","React","import_jsx_runtime","SendAmount","flow","amount","setAmount","amountUsd","balanceText","balanceRaw","token","setMax","inputRef","moveCursorToEnd","el","len","setHasInteracted","overBalance","parsed","parseTokenAmount","hasInteracted","displayAmount","empty","formatUsd","e","v","stripped","React","CROSSD_TOKEN_ADDRESSES","address","isCrossdTokenAddress","CHAIN_BADGE_ICONS","getChainBadgeUrl","env","chainId","chains","CHAIN_IDS","PORTRAIT_API","getPortraitUrl","address","opts","size","bg","format","base","bgParam","import_jsx_runtime","SearchIcon","size","className","PasteIcon","ClearIcon","DownArrowIcon","SuccessCircleIcon","ErrorCircleIcon","import_jsx_runtime","displayName","account","fallbackIndex","avatarFallback","label","normalizeAddress","address","SendRecipient","flow","recipient","setRecipient","fieldErrors","recentAddresses","accounts","walletAddress","resolvedEnv","selectRecipient","myAccounts","a","hasMyAccounts","accountByAddress","index","tab","setTab","inputRef","portraitBg","handlePaste","text","filled","valid","isAddress","e","ClearIcon","PasteIcon","name","portrait","getPortraitUrl","shortenAddress","item","matchedAccount","import_jsx_runtime","SendResult","flow","status","txHash","error","copyHash","hashCopied","openTransaction","isError","isConfirmed","ErrorCircleIcon","HashCard","SuccessCircleIcon","onCopy","onOpen","shortenAddress","CheckIcon","import_jsx_runtime","PLACEHOLDER","SendReview","flow","token","reviewRawAmount","reviewRecipient","accountName","accounts","walletAddress","networkName","nativeSymbol","nativeDecimals","gasEstimate","gasLoading","gasError","insufficientGas","error","resolvedEnv","isNative","isNativeSendAsset","amountDisplay","formatTokenUnits","fromName","fromPortrait","getPortraitUrl","displayNativeSymbol","forceLegacyGas","isBscLegacyChainId","gasMode","getGasMode","estTxFeeWei","getEstTxFeeWei","fee","wei","formatWeiToNative","gasPrice","formatWeiToGwei","maxTotalText","sum","v","text","gasRows","getGasPriceWei","formatGasLimit","e","DownArrowIcon","row","React","import_jsx_runtime","SendTokenSelect","flow","query","setQuery","q","list","t","SearchIcon","e","key","badge","getChainBadgeUrl","price","formatWei","formatUsd","styleInject","import_jsx_runtime","TITLES","SendFlow","onClose","onBackToWallet","onConfirmSuccess","onSuccess","className","rest","handleSuccess","txHash","flow","useSendFlow","step","status","recipientValid","isAddress","amountParsed","parseTokenAmount","amountValid","sending","showBack","title","TrackingBoundary","cn","BackIcon","CloseIcon","SendTokenSelect","SendRecipient","SendAmount","SendReview","SendResult","Popover","import_vaul","import_jsx_runtime","WalletInfoTrigger","asChild","className","children","isMobile","theme","walletAddress","useWalletInfoContext","hasChildren","shouldAsChild","triggerContent","cn","WalletIcon","shortenAddress","React","Popover","import_vaul","import_bignumber","import_bignumber","NUMERIC","parseNumeric","value","BigNumber","cleaned","parsed","import_react_query","import_react","USER_BALANCE_QUERY_KEY","fetchBalance","baseUrl","chainId","walletAddress","signal","params","res","json","useTokenBalance","env","active","chains","CHAIN_IDS","chainIds","results","getApiBase","isLoading","r","isError","merged","import_react_query","import_react","FORGE_HOLDINGS_QUERY_KEY","FORGE_TOKEN_IMAGE_QUERY_KEY","fetchForgeHoldings","baseUrl","walletAddress","signal","res","json","fetchForgeTokenImage","tokenAddress","useForgeHoldings","env","active","data","rest","getApiBase","useForgeTokenImages","tokenAddresses","results","addr","map","r","import_react_query","import_react","TOKEN_STATS_QUERY_KEY","buildStatsKey","chainId","address","useTokenStats","env","enabled","data","rest","signal","baseUrl","getApiBase","res","json","map","stat","key","import_react_query","CROSSD_APR_QUERY_KEY","CROSSD_APR_STALE_TIME_MS","CROSSD_OVERVIEW_URL","cachedApr","aprRequest","fetchCrossdAprPercent","cachedApr","aprRequest","res","CROSSD_OVERVIEW_URL","json","ratio","value","CROSSD_APR_STALE_TIME_MS","formatCrossdAprPercent","aprPercent","magnitude","rounded","useCrossdApr","data","rest","CROSSD_APR_QUERY_KEY","import_react_query","import_react","TOKEN_INFO_QUERY_KEY","buildCategoryKey","chainId","address","useTokenInfo","env","enabled","data","rest","signal","baseUrl","getApiBase","res","json","categoryMap","map","info","key","infoMap","import_react","import_bignumber","useTotalValue","env","walletAddress","enabled","tokens","isBalanceLoading","useTokenBalance","statsMap","useTokenStats","total","BigNumber","t","stat","buildStatsKey","quantity","parseNumeric","price","value","totalUsd","displayValue","isLowerBound","formatTokenValueForDisplay","import_qrcode","import_crossx_design_system","import_crossx_design_system","import_crossx_design_system","import_jsx_runtime","ONE_WALLET_SVG","ONE_WALLET_ICON","CROSSxIcon","MetaMaskIcon","BinanceIcon","Verse8Icon","TronIcon","GoogleIcon","AppleIcon","CloseIcon","WALLET_REGISTRY","CROSSxIcon","MetaMaskIcon","BinanceIcon","Verse8Icon","TronIcon","resolveWallet","id","SOCIAL_REGISTRY","GoogleIcon","AppleIcon","resolveSocial","STORAGE_KEY","SOCIAL_IDS","recordSelectedWallet","id","readSelectedWalletName","WALLET_REGISTRY","handleOutlinkClick","url","ctx","onOutlink","maybe","err","openInNewTab","target","v","win","finalize","result","React","import_vaul","import_react","import_bignumber","ISO_DATE_PREFIX_RE","pickAccruedCumulativeEarned","records","byDay","latestKey","key","cumulative","r","x","b","today","isPointsWithdrawPosition","position","formatWalletAddress","address","shortenTransactionHash","hash","rewardCardFitLevel","symbol","amount","len","displayCrossdSymbol","WALLET_ERROR_CUT_MARKERS","toDisplayWalletError","err","fallback","summary","marker","idx","line","WITHDRAW_ERC20_SELECTOR","encodeWithdrawErc20","poolId","id","poolIdWord","amountWord","formatGweiFromWei","value","BigNumber","formatGasLimit","formatNativeFeeFromWei","precision","formatPortfolioTokenAmount","raw","decimals","formatPortfolioDecimalAmount","DEFAULT_PORTFOLIO_FIAT","formatFiatFromUsd","usd","fiat","rate","formatPortfolioUsd","price","p","WalletPortfolioContext","useWalletPortfolioContext","ctx","usePortfolioFiat","conversionRatio","currencySymbol","DEFAULT_PORTFOLIO_FIAT","import_vaul","import_jsx_runtime","WalletPortfolioTrigger","asChild","children","walletAddress","useWalletPortfolioContext","hasChildren","shouldAsChild","triggerContent","shortenAddress","import_vaul","React","import_react_query","import_react","SectionStatusContext","useSectionStatusReporter","import_jsx_runtime","BackArrowIcon","size","className","RefreshIcon","InfoCircleIcon","size","className","ClipboardIcon","CheckIcon","SuccessCheckIcon","ExternalLinkIcon","size","className","import_react_query","import_react","REWARD_POOLS_QUERY_KEY","useRewardPools","env","enabled","data","rest","signal","baseUrl","getApiBase","res","json","crossPools","p","gamePools","import_react_query","import_react","USER_DEPOSITS_QUERY_KEY","useUserDeposits","env","walletAddress","enabled","isEnabled","data","rest","signal","baseUrl","getApiBase","res","json","map","deposit","import_react_query","import_react","REWARDS_WEB","POINTS_WEB","DEX_WEB","STAKING_WEB_HOME","STAKING_WEB","GAME_SWAP_WEB","APP_LAUNCHER_ICONS_BASE","CROSS_DEFI_API","CROSSD_WEB","CROSSD_POSITION_MANAGER","CROSSD_WCROSS","TOKEN_INFO_QUERY_KEY","NATIVE_TOKEN_SENTINEL_ADDRESS","useTokenInfo","env","enabled","crossChainId","CHAIN_IDS","data","rest","signal","baseUrl","getApiBase","res","json","byChainAddr","byChainSym","addrMap","symMap","t","addr","sym","chainSymKey","address","symbol","opts","chainOnly","byChain","byAddr","byChainSymbol","bySym","fallback","React","import_jsx_runtime","SectionCard","sectionKey","title","icon","iconFit","isEmpty","loading","children","reporter","useSectionStatusReporter","import_bignumber","import_jsx_runtime","SECTION_TITLE","isPositiveRawAmount","raw","BigNumber","RewardsSection","env","walletAddress","onOutlink","useWalletPortfolioContext","fiat","usePortfolioFiat","hasAddress","allPools","poolsLoading","useRewardPools","depositMap","depositsLoading","useUserDeposits","getTokenImage","useTokenInfo","getRewardsUrl","pool","action","REWARDS_WEB","sectionIcon","APP_LAUNCHER_ICONS_BASE","visiblePools","userDeposit","deposited","claimable","SectionCard","rewardRaw","withdrawUrl","claimUrl","depositIcon","firstRewardToken","firstRewardIcon","rewardSymbols","rt","poolLabel","rewardAmount","formatPortfolioTokenAmount","rewardSymbol","depositedUsd","formatPortfolioUsd","rewardUsd","canClaim","e","handleOutlinkClick","ExternalLinkIcon","React","import_react_query","import_react","BALANCE_OF_SELECTOR","TOTAL_SUPPLY_SELECTOR","DECIMALS_SELECTOR","RPC_TIMEOUT_MS","rpcCall","rpcUrl","method","params","signal","controller","timeout","res","json","encodeAddressArg","address","toBigIntOrUndefined","hex","ethCall","to","data","from","readPair","account","pair","balanceHex","supplyHex","decimalsHex","balance","decimalsRaw","fetchCrossRpcUrl","env","chainId","CHAIN_IDS","walletApi","getApiBase","readLpBalancesViaRpc","pairAddresses","entries","out","addr","info","WITHDRAW_TOKEN_META_QUERY_KEY","DEPOSIT_TOKEN_SELECTOR","SYMBOL_SELECTOR","DECIMALS_SELECTOR","decodeAbiString","hex","body","length","bytes","out","decodeAbiAddress","address","readDepositTokenMeta","env","poolAddresses","signal","rpcUrl","fetchCrossRpcUrl","entries","pool","tokenAddress","ethCall","symbolHex","decimalsHex","symbol","decimalsRaw","toBigIntOrUndefined","addr","meta","useWithdrawPositions","walletAddress","enabled","isEnabled","allPools","poolsLoading","useRewardPools","deposits","depositsLoading","useUserDeposits","poolMap","map","positiveDeposits","d","unknownPoolAddresses","rpcMeta","rpcMetaLoading","deposit","key","React","import_jsx_runtime","GAS_ESTIMATE_DEBOUNCE_MS","formatFeeValue","estimate","status","nativeSymbol","feePerGas","formatNativeFeeFromWei","formatGasMetric","value","unit","TokenBadge","position","size","WithdrawDialog","open","positions","initialPoolAddress","walletAddress","txHash","error","copied","estimateWithdraw","onClose","onConfirm","onCopyHash","trackFunnel","gasEstimate","setGasEstimate","gasStatus","setGasStatus","gasRequestRef","debounceRef","lastEstimateKeyRef","selected","key","p","depositedRaw","decimals","resetGas","estimateKey","requestId","isSubmitting","priorityFee","formatGweiFromWei","maxFee","gasLimit","formatGasLimit","depositedDisplay","formatPortfolioTokenAmount","event","SuccessCheckIcon","shortenTransactionHash","CheckIcon","ClipboardIcon","rewardCardFitLevel","formatWalletAddress","React","import_react_query","import_react_query","REWARD_FACTORY_INFO_QUERY_KEY","useRewardFactoryInfo","env","enabled","data","rest","signal","baseUrl","getApiBase","res","json","WALLET_PORTFOLIO_QUERY_PREFIX","invalidateWalletPortfolioQueries","queryClient","query","toHexAddress","address","useWithdrawFlow","section","env","walletAddress","sendTransaction","estimateGas","useWalletPortfolioContext","hasAddress","canWithdrawInline","queryClient","routerAddress","useRewardFactoryInfo","rewardRouterAddress","isOpen","setIsOpen","initialPoolAddress","setInitialPoolAddress","status","setStatus","txHash","setTxHash","error","setError","copied","setCopied","copyTimerRef","funnelFiredRef","trackFunnelOnce","funnelStep","funnelOptions","trackDappUiFunnel","buildWithdrawTransaction","poolId","amountRaw","encodeWithdrawErc20","CHAIN_IDS","invalidatePortfolioQueries","invalidateWalletPortfolioQueries","openWithdraw","poolAddress","closeWithdraw","estimateWithdraw","position","tx","from","copyHash","confirmWithdraw","estimate","hash","err","toDisplayWalletError","normalizeFailureReason","import_jsx_runtime","SECTION_TITLE","PointsSection","env","walletAddress","onOutlink","useWalletPortfolioContext","fiat","usePortfolioFiat","hasAddress","getTokenImage","getNativeToken","useTokenInfo","flow","useWithdrawFlow","positions","positionsLoading","useWithdrawPositions","pointPositions","isPointsWithdrawPosition","p","sectionIcon","APP_LAUNCHER_ICONS_BASE","pointsUrl","POINTS_WEB","SectionCard","position","depositedUsd","formatPortfolioUsd","formatPortfolioTokenAmount","e","handleOutlinkClick","ExternalLinkIcon","WithdrawDialog","React","import_react_query","STAKE_INFO_QUERY_KEY","useStakeInfo","env","walletAddress","enabled","isEnabled","data","rest","signal","baseUrl","getApiBase","res","json","import_react_query","STAKE_REWARDS_QUERY_KEY","REWARDS_HISTORY_LIMIT","useStakeLatestReward","env","walletAddress","enabled","isEnabled","data","rest","signal","baseUrl","getApiBase","res","json","pickAccruedCumulativeEarned","import_react_query","STAKING_ONCHAIN_QUERY_KEY","useStakingOnchain","walletAddress","reader","enabled","isEnabled","data","rest","import_react","import_bignumber","isCrossSymbol","symbol","isPositivePrice","price","BigNumber","useCrossUsdPrice","env","walletAddress","enabled","allPools","useRewardPools","rewardCrossPrice","pool","token","needFallback","tokens","useTokenBalance","statsMap","useTokenStats","crossChainId","CHAIN_IDS","crossToken","t","stat","buildStatsKey","import_bignumber","import_jsx_runtime","formatApr","apr","value","BigNumber","StakeSection","env","walletAddress","onOutlink","stakingRewardsReader","useWalletPortfolioContext","fiat","usePortfolioFiat","hasAddress","stakeInfo","stakeLoading","useStakeInfo","latestReward","aprAccrued","rewardLoading","useStakeLatestReward","onchain","useStakingOnchain","crossUsdPrice","useCrossUsdPrice","getNativeToken","useTokenInfo","nativeSymbol","sectionTitle","sectionIcon","APP_LAUNCHER_ICONS_BASE","manageUrl","STAKING_WEB","totalStake","rewardsEarned","principal","accrued","daysSinceFirstStake","myApr","hasStake","stakedUsd","formatPortfolioUsd","rewardsEarnedUsd","SectionCard","formatPortfolioDecimalAmount","e","handleOutlinkClick","ExternalLinkIcon","React","import_react_query","GAME_SWAP_POOLS_QUERY_KEY","useGameSwapPools","env","walletAddress","enabled","isEnabled","data","rest","signal","baseUrl","getApiBase","res","import_react_query","GAME_SWAP_LP_BALANCES_QUERY_KEY","useGameSwapLpBalances","env","walletAddress","pairAddresses","reader","sortedPairs","a","isEnabled","data","rest","signal","result","readLpBalancesViaRpc","normalized","addr","info","import_bignumber","import_jsx_runtime","SECTION_TITLE","DEFAULT_DECIMALS","LP_DUST_THRESHOLD","BigNumber","isCross","symbol","tokenDecimals","decimals","poolCrossValue","pool","crossOnA","reserve","crossAmount","buildRow","info","fiat","nativeSymbol","tokenASymbol","tokenBSymbol","lpDecimals","sharePercent","valueText","balance","totalSupply","ratio","positionCross","crossUsd","formatFiatFromUsd","GameSwapSection","env","walletAddress","onOutlink","lpBalanceReader","useWalletPortfolioContext","usePortfolioFiat","hasAddress","pools","poolsLoading","useGameSwapPools","pairAddresses","balances","balancesLoading","useGameSwapLpBalances","getNativeToken","useTokenInfo","sectionIcon","APP_LAUNCHER_ICONS_BASE","hasBalances","rows","built","row","getManageUrl","tokenAddress","GAME_SWAP_WEB","SectionCard","manageUrl","pairLabel","tokenAIcon","tokenBIcon","formatPortfolioTokenAmount","e","handleOutlinkClick","ExternalLinkIcon","import_bignumber","import_react","import_react_query","FORGE_POOLS_QUERY_KEY","useForgePools","env","walletAddress","enabled","hasAddress","data","rest","baseUrl","getApiBase","res","pools","import_react_query","import_react","FORGE_TOKEN_DETAIL_QUERY_KEY","fetchForgeTokenDetail","baseUrl","tokenAddress","res","useForgeTokenDetails","env","tokenAddresses","enabled","results","addr","getApiBase","detailMap","map","i","data","isLoading","r","import_jsx_runtime","SECTION_TITLE","LP_DECIMALS","FALLBACK_ICON","formatLp","raw","bn","BigNumber","formatOwnership","n","computeForgeLpUsd","pool","detail","crossUsdPrice","fiat","price","totalSupply","lpBalance","crossReserve","poolCrossValue","usd","formatFiatFromUsd","ForgeSection","env","walletAddress","onOutlink","useWalletPortfolioContext","usePortfolioFiat","hasAddress","pools","isEmpty","isLoading","useForgePools","tokenAddresses","p","detailMap","useForgeTokenDetails","useCrossUsdPrice","getForgeTokenUrl","DEX_WEB","sectionIcon","APP_LAUNCHER_ICONS_BASE","SectionCard","img","symbol","forgeTokenUrl","lpUsd","e","handleOutlinkClick","ExternalLinkIcon","import_react","import_bignumber","import_react_query","CROSSD_POSITIONS_QUERY_KEY","parseCrossdPositions","json","body","p","useCrossdPositions","env","walletAddress","enabled","hasAddress","data","rest","res","CROSS_DEFI_API","positions","import_react_query","import_react","CROSSD_POSITION_DETAIL_QUERY_KEY","parseDetail","json","body","detail","useCrossdPositionDetails","env","tokenIds","enabled","results","tokenId","res","CROSS_DEFI_API","map","r","i","data","React","import_jsx_runtime","PREPARED_STALE_MS","FALLBACK_TOKEN_ICON","formatFeeValue","estimate","isEstimating","nativeSymbol","feePerGas","formatNativeFeeFromWei","formatGasMetric","value","unit","CrossdWithdrawDialog","open","status","target","token0Icon","token1Icon","token0Symbol","token1Symbol","txHash","error","copied","hasEstimator","prepareWithdraw","confirmWithdraw","onClose","onCopyHash","trackFunnel","prepared","setPrepared","preparedAt","setPreparedAt","generationRef","generation","result","isSubmitting","runPrepareAndConfirm","next","isStale","position","detail","token0","token1","symbol0","symbol1","decimals0","decimals1","icon0","icon1","pairLabel","expectedAmount0Display","formatRawTokenAmount","expectedAmount1Display","priorityFee","formatGweiFromWei","maxFee","gasLimit","formatGasLimit","event","SuccessCheckIcon","shortenTransactionHash","CheckIcon","ClipboardIcon","rewardCardFitLevel","React","import_react_query","DECREASE_LIQUIDITY_SELECTOR","COLLECT_SELECTOR","UNWRAP_WETH9_SELECTOR","SWEEP_TOKEN_SELECTOR","MULTICALL_SELECTOR","MAX_UINT128","DEADLINE_SECONDS","SLIPPAGE_NUMERATOR","SLIPPAGE_DENOMINATOR","applyWithdrawSlippage","amount","isSameAddress","a","b","uintWord","value","label","hex","addressWord","address","encodeDecreaseLiquidity","p","encodeCollect","encodeUnwrapWeth9","amountMinimum","recipient","encodeSweepToken","token","encodeMulticall","calls","bodies","call","raw","padded","offset","offsets","body","decodeDecreaseLiquidityResult","composeV3WithdrawCalldata","input","token0IsWcross","token1IsWcross","useNativeCross","simulateDecreaseLiquidity","env","args","signal","rpcUrl","fetchCrossRpcUrl","data","encodeDecreaseLiquidity","result","ethCall","decodeDecreaseLiquidityResult","REFETCH_DELAYS_MS","toHexAddress","address","useCrossdWithdrawFlow","env","walletAddress","sendTransaction","estimateGas","useWalletPortfolioContext","canWithdrawInline","queryClient","isOpen","setIsOpen","target","setTarget","status","setStatus","txHash","setTxHash","error","setError","copied","setCopied","withdrawnTokenIds","setWithdrawnTokenIds","copyTimerRef","refetchTimersRef","flowIdRef","funnelFiredRef","trackFunnelOnce","funnelStep","funnelOptions","trackDappUiFunnel","invalidatePortfolioQueries","invalidate","invalidateWalletPortfolioQueries","delay","openWithdraw","next","closeWithdraw","prepareWithdraw","flowId","owner","tokenIdRaw","liquidityRaw","token0","token1","tokenId","liquidity","deadline","DEADLINE_SECONDS","positionManager","CROSSD_POSITION_MANAGER","simulated","simulateDecreaseLiquidity","data","composeV3WithdrawCalldata","applyWithdrawSlippage","CROSSD_WCROSS","tx","CHAIN_IDS","estimate","err","toDisplayWalletError","confirmWithdraw","prepared","hash","withdrawnTokenId","prev","normalizeFailureReason","copyHash","import_jsx_runtime","SECTION_TITLE","FALLBACK_TOKEN_ICON","CrossdPoolSection","env","walletAddress","onOutlink","useWalletPortfolioContext","fiat","usePortfolioFiat","hasAddress","positions","isLoading","useCrossdPositions","flow","useCrossdWithdrawFlow","visiblePositions","p","tokenIds","id","detailMap","useCrossdPositionDetails","poolWebUrl","CROSSD_WEB","getTokenImage","getTokenSymbol","getNativeToken","useTokenInfo","sectionIcon","APP_LAUNCHER_ICONS_BASE","CHAIN_ONLY","resolveTokenSymbol","token","listSymbol","display","displayCrossdSymbol","resolveTokenIcon","rawSymbol","targetPool","targetIcon0","targetIcon1","targetSymbol0","targetSymbol1","SectionCard","position","detail","pairLabel","feeBadge","icon0","icon1","positionValueUsd","formatFiatFromUsd","BigNumber","canWithdrawInline","e","handleOutlinkClick","ExternalLinkIcon","CrossdWithdrawDialog","PORTFOLIO_SECTIONS","styleInject","import_jsx_runtime","POLL_SEC","WalletPortfolioBody","env","theme","walletAddress","walletName","onBack","showHeader","autoRefresh","variant","className","onOutlink","lpBalanceReader","stakingRewardsReader","sendTransaction","estimateGas","conversionRatio","currencySymbol","sections","resolvedEnv","resolveEnvironment","isVisible","section","hasAddress","queryClient","sectionStatus","setSectionStatus","reportSectionStatus","key","status","prev","cur","unregisterSection","next","sectionStatusContext","countdown","setCountdown","spinning","setSpinning","copied","setCopied","copyTimerRef","invalidateAll","invalidateWalletPortfolioQueries","triggerRefresh","id","close","portraitUrl","getPortraitUrl","handleCopyAddress","contextValue","expectedSections","PORTFOLIO_SECTIONS","allSettled","allEmpty","anyContent","showLoader","inner","BackArrowIcon","formatWalletAddress","CheckIcon","CopyIcon","RefreshIcon","RewardsSection","PointsSection","StakeSection","GameSwapSection","ForgeSection","CrossdPoolSection","WalletPortfolioContext","SectionStatusContext","TrackingBoundary","cn","import_jsx_runtime","WalletPortfolioContent","className","env","theme","walletAddress","close","onOutlink","lpBalanceReader","stakingRewardsReader","useWalletPortfolioContext","cn","WalletPortfolioBody","import_jsx_runtime","WalletPortfolioRoot","env","theme","walletAddress","propOpen","onOpenChange","onOutlink","lpBalanceReader","stakingRewardsReader","sendTransaction","estimateGas","conversionRatio","currencySymbol","children","resolvedEnv","resolveEnvironment","isControlled","internalOpen","setInternalOpen","open","handleOpenChange","next","close","contextValue","WalletPortfolioContext","WalletPortfolio","WalletPortfolioTrigger","WalletPortfolioContent","React","import_react_dom","import_bignumber","React","KEYBOARD_OPEN_THRESHOLD_PX","isTextInputTarget","target","useSoftKeyboardOpen","isOpen","setIsOpen","viewport","readHeight","baselineHeight","syncFromViewport","height","resetBaseline","handleFocusIn","event","handleFocusOut","React","useExitDelay","value","ms","held","setHeld","timer","import_jsx_runtime","HistoryIcon","size","className","SlidersIcon","ArrowDownIcon","ArrowRightIcon","React","import_bignumber","isPositivePrice","value","formatFallbackAmount","BigNumber","canUseOneToOneFallback","input","getFallbackQuote","fromPrice","toPrice","sourceAmount","amount","parseNumeric","createBridgeRepository","getQuote","submitBridge","AMOUNT_MAX_INTEGER_DIGITS","AMOUNT_MAX_DECIMAL_DIGITS","TERMS_ACCEPTED_COOKIE_NAME","TERMS_ACCEPTED_COOKIE_MAX_AGE","sanitizeAmountInput","value","cleaned","firstDot","dotIndex","intPart","decPart","hasAcceptedTermsCookie","cookie","persistAcceptedTermsCookie","secure","resolveAutoSlippage","priceImpact","impact","trunc2","calc","normalizeCrossSymbol","symbol","s","isUniswapV3Pair","a","b","symbols","sameToken","firstToken","tokens","selectDefaultToToken","from","explicit","getToTokens","available","fromIndex","token","nextToken","buildInput","fromToken","toToken","fromAmount","toAmount","lastChangedBy","slippage","collectErrorFields","err","depth","errorLike","isUserRejection","haystack","isCrossxTypedDataSignatureFailure","bridgeErrorMessage","fallback","useBridgeFlow","options","initialFromToken","initialToToken","getApproval","approveBridge","history","initialSlippage","termsUrl","getQuote","submitBridge","onSubmitted","onSuccess","onFailed","onExit","repository","createBridgeRepository","step","setStep","setFromToken","setToToken","setFromAmount","setToAmount","setLastChangedBy","setSlippage","summary","setSummary","slippageLocked","effectiveSlippage","quoteError","setQuoteError","status","setStatus","error","setError","submitted","setSubmitted","localHistory","setLocalHistory","termsOpen","setTermsOpen","termsAccepted","setTermsAccepted","approvalInfo","setApprovalInfo","approvalSuccessOpen","setApprovalSuccessOpen","pendingSubmitInput","setPendingSubmitInput","submitToast","setSubmitToast","successDialogOpen","setSuccessDialogOpen","hasNewHistory","setHasNewHistory","quoteReqIdRef","toastTimerRef","submitLockRef","approvalLockRef","funnelOpenedRef","funnelTokenSelectedRef","funnelAmountEnteredRef","funnelSubmittedRef","funnelApprovedRef","trackDappUiFunnel","clearToastTimer","showSubmitToast","next","nextFrom","nextTo","setAmount","source","sanitized","resetQuoteState","selectFromToken","selectToToken","availableToTokens","setMax","swapTokens","resetInputAfterSubmit","current","reqId","input","result","getCurrentInput","runSubmit","nextApproval","info","items","item","normalizeFailureReason","message","submit","confirmTerms","cancelTerms","confirmApproval","cancelApproval","continueAfterApproval","dismissSuccessDialog","setBridgeStep","back","mergedHistory","remoteItem","styleInject","import_jsx_runtime","ThemeContext","useThemeAttr","useDetectTheme","ref","theme","setTheme","el","read","closest","observer","SLIPPAGE_PRESETS","HISTORY_FILTERS","formatSlippageLabel","value","effective","sanitizeSlippageInput","cleaned","integer","decimalParts","decimal","tokenKey","token","formatBalance","n","formatUsd","amount","price","clampDecimals","maxDp","intPart","fracPart","truncated","exceedsBalance","parseNumeric","balance","formatRateDecimal","trimmed","BigNumber","integerWithCommas","shortHash","hash","formatHistoryTimestamp","timestamp","date","pad","formatHistoryTokenAmount","formatHistoryUsd","parsed","getHistoryStatus","item","historyMatchesFilter","filter","historyStatusLabel","status","chainLabel","chainId","sameBridgeAsset","a","b","inferPathType","from","to","isDirectSwapType","pathType","isZeroAmount","getQuoteFieldErrors","message","normalized","defaultSummary","flow","fromAmount","toAmount","showBridge","showSwap","TokenIcon","size","iconUrl","nextToken","choices","current","index","BridgeAmountPanel","label","usd","onAmountChange","onTokenClick","locked","event","amountInputSize","ChevronDownIcon","formatEmbeddedAmount","formattedInteger","length","WalletGlyph","EmbeddedAmountPanel","onMax","BridgeSlippageSettings","onClose","titleId","portalTarget","setPortalTarget","selectedPreset","preset","customDraft","setCustomDraft","hasCustomDraft","customValue","exceedsMax","highSlippage","selectPreset","applyCustom","handleKeyDown","CloseIcon","BridgeTermsModal","env","checked","setChecked","remoteTermsUrl","useRemoteConfigValue","config","resolveEnvValue","termsUrl","DEFAULT_ONEUSD_TERMS_URL","visible","animState","useExitDelay","CopyButton","disabled","copied","setCopied","BridgeApproveModal","openApproval","approval","isApproving","BridgeApprovalSuccessModal","isContinuing","ExchangeRate","fromToken","toToken","exchangeRate","Collapse","BridgeStatusToast","BridgeErrorToast","setVisible","timer","successTitle","BridgeSuccessModal","submitted","InfoTokenGlyph","hasImage","InfoRowValue","row","InfoRows","rows","getInfoTitle","summary","hasBridgeInfo","hasSwapInfo","open","className","children","lastOpenChildren","BridgeDetails","feeDelegated","hasTxFee","infoTitle","infoOpen","setInfoOpen","txFeeOpen","setTxFeeOpen","RouteStepSections","normalizeEmbeddedSummary","formatAmountForStep","formatRateLine","forward","reversed","PRICE_IMPACT_WARNING","PRICE_IMPACT_DANGER","parsePercent","priceImpactTone","percent","IN_AMOUNT","OUT_AMOUNT","FEE_ROW","CONTEXT_ROW","IMPACT_ROW","ROUTE_ROW","toRouteSection","key","inRow","outRow","used","routeRow","routeTokens","routeText","meta","isFee","numeric","isFree","RouteStepList","steps","step","tokenIndex","tag","withImpactTone","tone","rateLine","feeDelegatedProp","includeTxFee","sections","section","first","last","sendToken","sendAmount","receiveToken","receiveAmount","txFeeInfo","gasMeta","multiHop","rest","showRate","EmbeddedBridgeDetails","EmbeddedFlowOverlays","toastError","EmbeddedFormView","isConnected","onRequestConnect","hasAmount","fieldErrors","insufficientBalance","fromError","insufficientLiquidity","toError","commonQuoteError","isSubmitting","isDisabled","actionLabel","next","ArrowDownIcon","HistoryCard","fiatAmount","ArrowRightIcon","HistoryView","setFilter","hasPending","visibleHistory","emptyMessage","DefaultFormView","slippageSettingsOpen","setSlippageSettingsOpen","swapRotation","setSwapRotation","poweredByUrl","hasPendingHistory","showSlippage","HistoryIcon","SlidersIcon","r","BridgeFlow","onBackToWallet","variant","titleProp","isConnectedProp","resolvedEnv","resolveEnvironment","isSoftKeyboardOpen","useSoftKeyboardOpen","useBridgeFlow","rootRef","title","backAction","TrackingBoundary","cn","BackIcon","React","import_bignumber","import_react_query","TOKEN_HISTORY_QUERY_KEY","useTokenHistory","env","chainId","token","wallet","enabled","pageParam","signal","baseUrl","getApiBase","params","cursor","res","json","lastPage","last","React","DEFAULT_VISIBILITY","SECTION_KEYS","APPS_CONFIG_FILE","useTokenDetailVisibility","env","state","setState","alive","fetchRemoteConfig","config","raw","visibility","key","value","React","import_react_query","TOKEN_PRICE_HISTORY_QUERY_KEY","useTokenPriceHistory","env","chainId","token","interval","enabled","signal","baseUrl","getApiBase","params","res","json","import_jsx_runtime","UP_COLOR","DOWN_COLOR","CHART_HEIGHT","PAD_X","PAD_TOP","PAD_BOTTOM","INTERVALS","LONG_RANGE","useElementWidth","ref","width","setWidth","el","ro","entries","formatPointTime","unixSec","longRange","d","opts","buildGeometry","values","vals","max","min","maxIdx","minIdx","i","range","innerW","innerH","xFor","yFor","v","points","linePath","p","first","last","areaPath","TokenPriceChart","env","chainId","tokenAddress","interval","setInterval","hoverIdx","setHoverIdx","gradientId","plotRef","data","isLoading","isError","useTokenPriceHistory","series","a","b","geometry","hasData","currentIdx","currentValue","firstValue","changePct","isUp","color","changeText","whenText","handlePointerMove","e","rect","relX","ratio","idx","clearHover","hoverPoint","maxPoint","minPoint","clampLabelX","x","formatPrice","tab","import_jsx_runtime","FALLBACK_TOKEN_ICON","HISTORY_DIRECTION_LABEL","historyAmountSize","text","formatRelativeTime","timestamp","ms","diffSec","min","hr","day","month","groupInteger","value","intPart","fracPart","grouped","i","fromEnd","floorTokenAmount2","raw","decimals","unit","whole","cents","hundredths","useFitFontSize","maxPx","minPx","containerRef","textRef","fontSize","setFontSize","container","el","measure","available","cs","probe","natural","next","ro","formatSupply","bn","parseNumeric","BigNumber","GlobeIcon","TelegramIcon","XIcon","MediumIcon","TokenDetailView","env","isMobile","token","walletAddress","stat","tokenInfo","showBalance","onBack","onClose","onSend","onOutlink","copied","setCopied","buildTxUrl","chainInfo","useExplorerUrlBuilder","visibility","useTokenDetailVisibility","data","isHistoryLoading","isHistoryError","fetchNextPage","hasNextPage","isFetchingNextPage","refetchHistory","useTokenHistory","historyItems","loadMoreRef","observer","entries","handleCopyAddress","balanceText","fiatText","formatTokenValue","balanceDisplay","balanceLeftRef","balanceAmountRef","balanceFontSize","networkName","useChainDisplay","explorerBase","deployed","socialLinks","links","outlink","e","url","origin","txHash","handleOutlinkClick","BackIcon","CloseIcon","TokenPriceChart","ExternalLinkIcon","shortenAddress","CheckIcon","CopyIcon","link","_","item","idx","toIsBridge","fromIsBridge","direction","isInflow","counterparty","amount","txUrl","relative","rowInner","key","cn","React","ONE_POP_WEB_BASE","onePopLandingUrl","env","page","base","ONE_USD_BRIDGE_URL","oneUsdBridgeUrl","import_jsx_runtime","OnePopActionIcon","size","className","OnePopSendIcon","OnePopClaimIcon","OnePopActivityIcon","OnePopChevronRightIcon","XBadgeIcon","OnePopHandleIcon","OnePopInstantIcon","OnePopRefundIcon","OnePopLogo","ONEPOP_HERO_ART_SRC","OnePopHeroArt","styleInject","import_jsx_runtime","RECENT_ACTIVITY_LIMIT","STATUS_LABEL","formatAmount2","raw","decimals","value","negative","whole","cents","unit","hundredths","groupInteger","intPart","grouped","i","fromEnd","formatRelativeTime","timestamp","ms","diffSec","min","hr","day","month","withAt","handle","trimmed","OnePopBody","env","balanceDisplay","balanceIconUrl","isBalanceLoading","summary","logoSrc","heroSrc","onBack","onClose","onSend","onClaim","onActivity","onRepliesSeen","onBoard","onGetToken","getTokenSlot","activity","hasActivity","claimableCount","repliesWaitingCount","isLoading","xConnected","openLanding","page","onePopLandingUrl","handleSend","handleClaim","handleActivity","handleBoard","handleGetToken","oneUsdBridgeUrl","claimableTotal","recentActivity","handleImageError","e","BackIcon","OnePopLogo","CloseIcon","XBadgeIcon","OnePopHeroArt","OnePopChevronRightIcon","OnePopSendIcon","OnePopClaimIcon","OnePopActivityIcon","item","ActivityRow","FeatureRow","OnePopHandleIcon","OnePopInstantIcon","OnePopRefundIcon","icon","title","desc","label","initial","cn","React","import_jsx_runtime","OnePopGetTokenTrigger","_className","_children","props","ref","OnePopChevronRightIcon","import_jsx_runtime","ONRAMP_BUY_FAILED_MESSAGE","getPinnedOrder","env","chainId","symbol","chains","CHAIN_IDS","s","parseChainIdNum","caip2","isWithinGametokenSite","sites","origin","pathname","current","site","splitAmount","value","dotIdx","getAvatarGradient","address","hex","seed","h1","h2","useFitScale","dep","minScale","containerRef","valueRef","scale","setScale","container","el","measure","available","clone","natural","next","ro","WalletInfoContent","align","sideOffset","className","children","theme","isMobile","open","drawerDirection","modal","showBalance","showForgeToken","showGameToken","showQR","showBridge","qrLogoSrc","walletAddress","accountName","sendAccounts","profileImageUrl","connectorName","preferredTokens","close","onSelectWallet","onCopyAddress","onBuy","onDisconnect","disconnectLabel","termsUrl","termsLabel","privacyUrl","privacyLabel","portalStyle","navContent","navPosition","footerContent","view","setViewFromCtx","selectedSendToken","setSelectedSendToken","selectedDetailToken","setSelectedDetailToken","showPortfolio","showOnePop","onePopSummary","onOnePopOpen","onOnePopSend","onOnePopClaim","onOnePopActivity","onOnePopRepliesSeen","onePopLogoSrc","onePopGetTokenSlot","onePopHeroSrc","portfolioTitle","portfolioSections","showTotalAssets","totalAssetsLabel","sendTransaction","getTransactionReceipt","estimateGas","onOutlink","lpBalanceReader","stakingRewardsReader","bridgeTokens","bridgeHistory","getBridgeQuote","getBridgeToTokens","getBridgeApproval","approveBridge","submitBridge","onReceive","onSend","onBuyDisabledMessage","onBridgeDisabledMessage","onSendDisabledMessage","useWalletInfoContext","detectedWalletName","setDetectedWalletName","readSelectedWalletName","isInAppBrowser","displayAccountName","actionToast","setActionToast","actionToastTimerRef","showActionToast","message","buyDisabledByMessage","handleBuyClick","result","bridgeDisabledByMessage","sendDisabledByMessage","rawTokens","isLoading","useTokenBalance","statsMap","useTokenStats","crossdAprPercent","useCrossdApr","categoryMap","infoMap","useTokenInfo","showTotal","totalDisplay","totalIsLowerBound","totalLoading","useTotalValue","totalHidden","setTotalHidden","footerRef","footerHeight","setFooterHeight","forgeHoldings","isForgeLoading","useForgeHoldings","forgeTokenAddresses","h","forgeImageMap","useForgeTokenImages","tokens","pinned","preferred","rest","prefKeyMap","t","i","token","isPinned","tokenKey","prefIdx","balance","parseNumeric","BigNumber","buildCategoryKey","a","b","aIdx","bIdx","statA","buildStatsKey","statB","valueA","sortedForgeHoldings","balanceAddressSet","hasTransitioned","sendToast","setSendToast","sendToastTimerRef","toastChainId","sendToastUrl","useTransactionExplorerUrl","setView","v","sendInitialStep","setSendInitialStep","handleBackToWallet","handleSelectSendToken","initialStep","handleSelectTokenDetail","handleOnePopOpen","handleBackFromDetail","handleSendFromDetail","remoteBridgeWebUrl","useRemoteConfigValue","config","resolveEnvValue","remoteGametokenSites","handleBridgeClick","handleSendSubmitted","info","shortenAddress","handleConfirmSendSuccess","txHash","handleSendFailed","effectiveReceive","effectiveSend","first","handleSendClick","scrollRef","stop","e","copied","setCopied","copyTimerRef","accountModalOpen","setAccountModalOpen","handleClose","handleKey","timer","footerEl","updateFooterHeight","observer","handleCopyAddress","handleImageLoad","avatarStyle","resolvedProfileUrl","getPortraitUrl","totalSplit","totalBodyRef","totalValueRef","totalScale","legalLinks","handleOutlinkClick","defaultDisconnectButton","DisconnectIcon","showDefaultQuickActions","sendInActionRow","sendActionButton","cn","SendActionIcon","defaultQuickActions","BuyActionIcon","BridgeActionIcon","QRIcon","OnePopActionIcon","PortfolioActionIcon","walletView","prev","EyeClosedIcon","EyeOpenIcon","SendSolidIcon","_","stat","change","isPositive","chainBadgeUrl","getChainBadgeUrl","shouldShowApr","isCrossdTokenAddress","formatCrossdAprPercent","formatPrice","formatWei","formatTokenValue","holding","imgUrl","portfolioView","WalletPortfolioBody","sendView","SendFlow","n","resolvedBridgeTokens","price","formatRawTokenAmount","bridgeView","BridgeFlow","tokenDetailView","TokenDetailView","onePopToken","crossChainId","onePopBalanceDisplay","onePopView","OnePopBody","isSwitchableAccount","receiveAccountContent","ChevronDownIcon","qrView","QRLogoIcon","CheckIcon","CopyIcon","isSubView","effectiveFooter","accountTextBlock","contentBody","BackIcon","CloseIcon","ToastCheckIcon","contentStyle","keepOpenForWidgetShell","event","target","TrackingBoundary","React","WalletInfoNav","position","children","setNavContent","setNavPosition","useWalletInfoContext","React","WalletInfoFooter","children","setFooterContent","useWalletInfoContext","styleInject","styleInject","styleInject","styleInject","styleInject","styleInject","import_jsx_runtime","WalletInfoRoot","env","theme","mobileBreakpoint","drawerDirection","modal","showBalance","showForgeToken","showGameToken","showQR","showBridge","qrLogoSrc","walletAddress","accountName","sendAccounts","profileImageUrl","connectorId","connectorNameProp","connectorIconUrlProp","preferredTokens","onSelectWallet","onCopyAddress","onBuy","onBuyDisabledMessage","onBridgeDisabledMessage","onSendDisabledMessage","onDisconnect","disconnectLabel","termsUrl","DEFAULT_TERMS_URL","termsLabel","privacyUrl","DEFAULT_PRIVACY_URL","privacyLabel","propOpen","onOpenChange","showPortfolio","showOnePop","onePopSummary","onOnePopOpen","onOnePopSend","onOnePopClaim","onOnePopActivity","onOnePopRepliesSeen","onePopGetTokenSlot","onePopLogoSrc","onePopHeroSrc","portfolioTitle","portfolioSections","showTotalAssets","totalAssetsLabel","sendTransaction","getTransactionReceipt","estimateGas","onOutlink","lpBalanceReader","stakingRewardsReader","bridgeTokens","bridgeHistory","getBridgeQuote","getBridgeToTokens","getBridgeApproval","approveBridge","submitBridge","onReceive","onBridge","onSend","style","children","resolvedEnv","resolveEnvironment","registryMeta","CONNECTOR_REGISTRY","connectorName","connectorIconUrl","isMobile","useMediaQuery","resolvedDirection","isSideDrawer","resolvedModal","isControlled","internalOpen","setInternalOpen","open","handleOpenChange","next","close","useHistoryDismiss","navContent","setNavContent","navPosition","setNavPosition","footerContent","setFooterContent","view","setView","selectedSendToken","setSelectedSendToken","selectedDetailToken","setSelectedDetailToken","setDappUiAnalyticsUser","handleDisconnect","clearRecentSendAddresses","contextValue","WalletInfoContext","WalletInfo","WalletInfoTrigger","WalletInfoContent","WalletInfoNav","WalletInfoFooter","React","React","import_react","RelayDepositContext","useRelayDepositContext","ctx","React","import_react","RelayShellContext","useRelayShellContext","ctx","import_react","createRelayWizardStore","snapshot","listeners","next","listener","RelayWizardStoreContext","useRelayWizardStore","store","useRelayWizardBundle","bundle","setBundle","styleInject","import_jsx_runtime","DepositStatus","minNotice","arrivalStatus","arrived","liveMessage","remaining","DepositChecklist","onlySend","showWalletCreditNote","import_react","zeroAddress","ADDRESS_RE","isAddress","value","formatUnits","decimals","negative","s","intPart","fracPart","body","parseUnits","m","sign","combined","import_react","ONEUSD_LOGO","PONEUSD_LOGO","USDT_LOGO","USDC_LOGO","bundledTokenLogo","symbol","import_jsx_runtime","monogram","label","trimmed","Badge","size","bg","fg","children","MonogramBadge","text","OfficialIcon","viewBox","EthereumIcon","clip","BnbChainIcon","BaseIcon","ArbitrumIcon","PolygonIcon","gradient","SolanaIcon","ChainIcon","chainId","chainName","TokenIcon","symbol","logoUrl","resolvedLogo","bundledTokenLogo","import_qrcode","import_jsx_runtime","DepositAddressSkeleton","buildPaymentUri","params","depositAddress","originChainId","originCurrency","amountWei","isNative","query","buildSolanaPayUri","splMint","humanAmount","q","DepositStepView","originChainName","amount","tokenSymbol","tokenDecimals","kind","depositState","onDeposit","canDeposit","oneClickEnabled","copied","setCopied","compactView","setCompactView","addressTooltipId","zeroAddress","isSvm","handleCopy","parsed","hasAmount","formatUnits","paymentUri","isSubmitting","isConfirming","isConfirmed","needsSwitch","txHash","reverted","error","ChainIcon","import_react","Popover","import_jsx_runtime","Dropdown","value","options","onChange","disabled","ariaLabel","id","generatedId","baseId","listboxId","open","setOpen","selectedIndex","o","activeIndex","setActiveIndex","buttonRef","optionRefs","isDisabled","selected","popoverTheme","setPopoverTheme","openList","closeList","refocus","commit","index","opt","handleButtonClick","handleButtonKeyDown","e","i","activeOptionId","next","ChevronDownIcon","isSelected","isActive","el","import_jsx_runtime","formatQuoteAmount","raw","decimals","formatUnits","QuotePreview","quote","isLoading","error","humanAmount","originSymbol","destination","import_react","explorers","explorerTokenLink","chainId","address","e","chainDisplayNames","chainDisplayName","explorerTxLink","txHash","import_jsx_runtime","distinctChains","catalog","seen","origin","chainId","chainName","tokensForChain","o","formatBalance","value","decimals","s","formatUnits","dot","SourceForm","onChangeOrigin","amount","onChangeAmount","showAmountInput","balance","balanceLoading","insufficientBalance","onMax","disabled","chains","tokenOptions","handleChainChange","next","firstToken","handleTokenChange","match","balanceDisplay","amountError","link","explorerTokenLink","Dropdown","c","ChainIcon","TokenIcon","e","import_jsx_runtime","DepositPanel","catalog","origin","onChangeOrigin","amount","onChangeAmount","showAmountInput","minNotice","amountHint","onMax","recipient","onChangeRecipient","showRecipientInput","recipientValid","balance","balanceLoading","insufficientBalance","targetOptions","selectedTarget","onChangeTarget","showTargetSelector","quote","quoteLoading","quoteError","depositAddress","depositAddressLoading","depositAddressError","arrivalStatus","amountSmallestUnits","depositState","onDeposit","canDeposit","oneClickEnabled","locked","onRequestConnect","showPicker","phase","isSubmitting","isConfirming","isConfirmed","needsSwitch","reverted","error","deliverSymbol","DepositStatus","DepositStepView","DepositAddressSkeleton","SourceForm","Dropdown","t","TokenIcon","e","QuotePreview","DepositChecklist","import_react","import_react_dom","import_jsx_runtime","HistoryEmptyState","requiresIdentity","BSC_CHAIN_ID","isTerminalFailure","status","orderStatusLabel","normalized","label","words","statusPillClassName","formatAmount","raw","decimals","formatUnits","displayAmount","full","maxDecimals","dot","trimmed","formatTimestamp","iso","d","shortHash","hash","HistoryAmount","amount","symbol","className","tooltipId","anchorRef","tooltipRef","tooltipOpen","setTooltipOpen","tooltipStyle","setTooltipStyle","positionTooltip","anchor","tooltip","anchorRect","tooltipRect","viewportPadding","gap","maxLeft","left","above","preferredTop","maxTop","top","computed","originOf","order","attributed","distinct","first","allocateProRata","totalRaw","deposits","total","amounts","sum","a","b","shares","allocated","StepRow","chainId","txHash","shared","missingLabel","link","explorerTxLink","chainDisplayName","cardsOf","outRaw","i","currency","routes","OrdersList","orders","isLoading","error","catalog","showTitle","expanded","setExpanded","toggle","key","prev","card","origin","o","deliveryTarget","t","outSymbol","outDecimals","bscUsdt","sent","sentSymbol","received","isOpen","when","statusLabel","missingTxLabel","receivedLabel","ChevronDownIcon","import_react","import_react","RelayApiError","status","message","body","createRelayClient","options","baseUrl","request","path","init","headers","res","err","text","parsedBody","errBody","catalog","req","target","params","orderId","user","standingForwarderFactoryAbi","standingForwarderAbi","forwarderFactoryComputeAbi","DEFAULT_TRUSTED_FACTORIES","sameAddress","a","b","isTrustedFactory","factory","trusted","t","mergeTrustedFactories","remote","seen","resolved","entry","isAddress","key","verifyForwarder","readContract","chainId","forwarder","recipient","version","derived","forwarderFactoryComputeAbi","e","verifyForwarderFromAnyFactory","d","depositAddressIsForwarder","depositAddress","BSC_CHAIN_ID","IDLE_STATE","primaryRecoverableToken","info","useRecoveryActions","opts","optsRef","wallet","connectedChainId","recipient","recipientKey","isAddress","states","setStates","setState","forwarder","next","prev","stateFor","busy","s","action","tokenAddress","key","verdict","verifyForwarder","deployTxHash","standingForwarderFactoryAbi","actionTxHash","standingForwarderAbi","err","e","k","useRecovery","client","createRelayClient","setInfo","loading","setLoading","error","setError","refreshNonce","setRefreshNonce","target","enabled","refetchInterval","cancelled","inFlight","isFirstFetch","refetchRecovery","result","RelayApiError","refetchTimer","refresh","n","actions","recover","recoveryState","FUNDS_AT_FORWARDER_STATUS","toAmount","raw","v","orderTarget","order","defaultSymbol","groupForwarders","entries","orders","e","byForwarder","entry","key","existing","group","symbols","target","d","b","groupsWithFunds","groups","g","t","countRecoverableTokens","sum","import_jsx_runtime","BSC_CHAIN_ID","propsToOpts","props","DEFAULT_IN_FLIGHT_REASON","LEGACY_SYMBOL","PRIMARY_ROW_KEY","otherTargetsNote","n","recoveryErrorMessage","error","normalized","TechnicalDetails","message","copyStatus","setCopyStatus","timer","copyDetails","copyLabel","toBigInt","value","human","decimals","formatUnits","shortHash","hash","buildRows","info","minimum","rows","token","balance","primaryBalance","hasExecuteRow","r","DepositRow","deposit","chainId","txHash","link","explorerTxLink","chainDisplayName","ForwarderSection","group","showTargetLabel","busy","state","onRecover","actingRowKey","rowKeyOf","balanceValue","headline","depositsExplainAll","unexplained","running","actingAction","row","stepText","action","d","key","acting","TokenIcon","RecoverFunds","loading","refresh","useRecovery","isLoading","actions","useRecoveryActions","setActingRowKey","entries","groups","groupsWithFunds","groupForwarders","hasFunds","refreshFunds","walletAddress","getBalance","gasBalance","setGasBalance","cancelled","b","React","Dialog","import_vaul","styleInject","import_jsx_runtime","INPUT_SCROLL_MARGIN","isTextInput","target","getInputScrollDelta","targetRect","scrollAreaRect","viewport","viewportTop","viewportBottom","safeTop","safeBottom","useKeyboardViewportInset","layoutViewportHeightRef","inset","setInset","update","layoutHeight","keyboardHeight","layoutShrink","keyboardOverlap","viewportHeight","current","useIsMobileViewport","mobileBreakpoint","query","isMobile","setIsMobile","mql","handler","e","ResponsiveShell","theme","drawerDirection","dialogWidth","drawerMaxWidth","drawerMinWidth","style","openProp","onOpenChange","children","internalOpen","setInternalOpen","isControlled","open","handleOpenChange","value","useHistoryDismiss","mergedStyle","sizeVars","contextValue","wrapper","RelayShellContext","ShellTrigger","asChild","dataAttributes","useRelayShellContext","T","ShellContent","className","ariaTitle","portalStyle","isInApp","isInAppBrowser","drawerRef","focusTimerRef","active","scrollArea","shouldAdjustForKeyboard","keyboardStyle","handleFocusCapture","event","delta","cn","import_jsx_runtime","DepositTabs","id","activeView","recoverableCount","onChange","tabs","view","label","event","currentIndex","nextIndex","nextView","RelayDepositContent","className","props","useRelayDepositContext","TrackingBoundary","ShellContent","RelayDepositBody","theme","useRelayShellContext","wizard","useRelayWizardBundle","tabsId","setActiveView","recoveryOpen","resolvedApiBaseUrl","wallet","catalog","catalogLoading","catalogError","origin","setOrigin","amount","setAmount","recipient","setRecipient","recipientValid","balance","balanceLoading","insufficientBalance","depositAddress","depositAddressLoading","depositAddressError","arrivalStatus","quote","quoteLoading","quoteError","canDeposit","deposit","depositState","targetOptions","selectedTarget","setSelectedTarget","showAmountInput","minNotice","showRecipientInput","showTargetSelector","showOneClick","showConnectPrompt","showRecipientHint","canRecover","recoveryTargetsLoading","recoveryTargets","recoveryOrders","historyOrdersLoading","historyOrdersError","refreshRecovery","trustedFactories","amountHint","amountSmallestUnits","onMax","RecoverFunds","OrdersList","HistoryEmptyState","DepositAddressSkeleton","DepositPanel","import_jsx_runtime","RelayDepositTrigger","asChild","children","TrackingBoundary","ShellTrigger","React","import_react","import_react","useDebouncedValue","value","delayMs","debounced","setDebounced","timer","AMOUNT_DEBOUNCE_MS","FORWARDER_CHAIN_ID","DIRECT_ROUTE","QUOTE_REFRESH_FALLBACK_MS","QUOTE_REFRESH_MIN_MS","quoteRefreshMs","ttl","ERC20_TRANSFER_ABI","toSmallestUnits","human","decimals","trimmed","value","parseUnits","useDepositAddress","opts","optsRef","client","createRelayClient","wallet","address","isConnected","connectedChainId","rawCatalog","setRawCatalog","catalogLoading","setCatalogLoading","catalogError","setCatalogError","cancelled","result","err","chains","filteredOrigins","allow","o","catalog","origin","setOriginState","defaultToken","match","localDepositError","setLocalDepositError","resetTxState","setTxHash","setIsConfirming","setIsConfirmed","setReverted","setTxSubmitError","setOrigin","next","isSvmOrigin","amountState","setAmountState","amount","setAmount","debouncedAmount","useDebouncedValue","smallestAmount","enteredAmount","recipientState","setRecipientState","recipientTouched","setRecipientTouched","recipient","setRecipient","recipientValid","isAddress","recipientKey","balance","setBalance","balanceLoading","setBalanceLoading","balanceNonce","setBalanceNonce","getBalance","token","zeroAddress","b","formatUnits","balanceValue","insufficientBalance","depositAddressResult","setDepositAddressResult","depositAddressLoading","setDepositAddressLoading","depositAddressError","setDepositAddressError","originChainId","originCurrency","targetSymbol","e","depositIntentKey","lastDepositIntentKeyRef","quote","setQuote","quoteLoading","setQuoteLoading","quoteError","setQuoteError","smallestAmountKey","timer","isFirstFetch","fetchQuote","nextDelay","isSwitching","setIsSwitching","isTxPending","setIsTxPending","txHash","txSubmitError","isConfirming","isConfirmed","reverted","waitForReceipt","notifiedHashRef","receipt","n","needsSwitch","isSubmitting","submitError","depositPhase","depositState","addressMatchesOrigin","walletCanDeposit","canDeposit","deposit","amountWei","isNative","targetAddress","verdict","verifyForwarderFromAnyFactory","depositAddressIsForwarder","hash","import_react","ORDERS_FALLBACK_POLL_MS","ORDERS_DEBOUNCE_MS","useRelayOrders","opts","optsRef","client","enabled","live","refetchInterval","recipientKey","isAddress","orders","setOrders","ordersLoading","setOrdersLoading","ordersError","setOrdersError","ordersInitialized","setOrdersInitialized","ordersForRecipient","setOrdersForRecipient","ordersRevision","setOrdersRevision","cancelled","isFirstFetch","debounceTimer","seq","requestsInFlight","refetch","mySeq","result","err","e","value","debouncedRefetch","eventSource","fallbackTimer","import_react","pickInitial","options","defaultTarget","match","t","useTargetTokens","catalogTargets","filter","allow","selectedSymbol","setSelectedSymbol","selected","setSelected","symbol","RELAY_API_BASE_URLS","resolveRelayApiBaseUrl","opts","resolveEnvironment","import_jsx_runtime","DEPOSIT_STATUS_POLL_MS","propsToOpts","props","target","wallet","resolvedApiBaseUrl","trustedFactories","requireWalletConnection","connectedWallet","isAddress","toSmallestUnits","human","decimals","trimmed","value","parseUnits","orderMatchesForwarder","order","forwarderVersion","targetSymbol","targetIsDefault","orderTarget","orderDepositKey","orderId","txHash","blockNumber","isFailureStatus","status","RelayWizardStateHost","open","children","store","createRelayWizardStore","RelayWizardStoreContext","RelayWizardEngine","useRelayDepositContext","showTargetSelector","hasValidConnectedWallet","activeView","setActiveView","recoveryOpen","setRecoveryOpen","next","current","trackDappUiFunnel","setTargetSymbol","resolveRelayApiBaseUrl","resolvedEnv","resolveEnvironment","remoteRelayConfig","useRemoteConfigValue","config","resolveEnvValue","resolveRelayForwarderFactories","effectiveTrustedFactories","mergeTrustedFactories","isConnected","catalog","catalogLoading","catalogError","origin","setOrigin","amount","setAmount","recipient","setRecipient","recipientValid","balance","balanceLoading","insufficientBalance","depositAddress","depositAddressResult","depositAddressLoading","depositAddressError","quote","quoteLoading","quoteError","canDeposit","deposit","depositState","useDepositAddress","targetOptions","selectedTarget","setSelectedTarget","useTargetTokens","showAmountInput","showRecipientInput","hasResolvedRecipient","controlledRecipient","showConnectPrompt","showRecipientHint","canRecover","recoveryClient","createRelayClient","recoveryByTarget","setRecoveryByTarget","recoveryTargetsLoading","setRecoveryTargetsLoading","recoveryNonce","setRecoveryNonce","refreshRecovery","n","depositConfirmed","ordersWatchEnabled","watchedOrders","watchedOrdersLoading","watchedOrdersError","watchedOrdersInitialized","ordersForRecipient","useRelayOrders","recoveryOrders","currentTarget","currentTargetSymbol","currentTargetIsDefault","currentForwarderVersion","matchingOrders","depositWatchBaselineRef","trackedOrderId","setTrackedOrderId","latestArrivalRaw","setLatestArrivalRaw","depositWatchKey","snapshotMatchesRecipient","currentOrderIds","currentDepositKeys","baseline","candidate","arrived","hasParsedArrival","key","depositKey","targets","cancelled","t","results","byTarget","i","info","recoveryTargets","e","recoverableCount","countRecoverableTokens","groupForwarders","pendingSweep","setPendingSweep","selectedRecovery","setSelectedRecovery","targetSyncPending","inFlight","expectedForwarder","refreshSelectedRecovery","raw","symbol","recoveryPollTimer","handleMax","formatUnits","addressShownRef","hasDepositAddress","minHuman","maxHuman","minSmallest","opsMin","catalogMin","minGuidanceHuman","pendingNotice","originDec","recDec","pendingInOrigin","remaining","bridgeTokenOrigin","bridgeDecimals","bridgeSymbol","token","latestArrivalAmount","effectiveMinimumAtForwarder","originDecimals","divisor","trackedOrder","activeOrder","activeOrderMeetsMinimum","recoveryMinimumReached","arrivalStatus","common","maxSmallest","enteredSmallest","belowMinimum","aboveMaximum","amountHint","showOneClick","amountSmallestUnits","bundle","import_jsx_runtime","RelayDepositRoot","props","theme","mobileBreakpoint","drawerDirection","dialogWidth","drawerMaxWidth","drawerMinWidth","style","openProp","onOpenChange","children","internalOpen","setInternalOpen","isControlled","open","handleOpenChange","value","RelayDepositContext","RelayWizardStateHost","ResponsiveShell","RelayDeposit","RelayDepositTrigger","RelayDepositContent","import_react","RelayHistoryContext","useRelayHistoryContext","ctx","React","import_jsx_runtime","RelayHistoryContent","className","props","useRelayHistoryContext","open","useRelayShellContext","trackDappUiFunnel","TrackingBoundary","ShellContent","RelayHistoryBody","theme","client","baseUrl","resolveRelayApiBaseUrl","createRelayClient","resolvedRecipient","hasValidRecipient","isAddress","catalog","setCatalog","catalogLoading","setCatalogLoading","catalogError","setCatalogError","onErrorRef","cancelled","result","err","error","orders","ordersLoading","ordersError","useRelayOrders","handleClick","e","anchor","OrdersList","HistoryEmptyState","import_jsx_runtime","RelayHistoryTrigger","asChild","children","TrackingBoundary","ShellTrigger","import_jsx_runtime","RelayHistoryRoot","props","theme","mobileBreakpoint","drawerDirection","dialogWidth","drawerMaxWidth","drawerMinWidth","style","open","onOpenChange","children","RelayHistoryContext","ResponsiveShell","RelayHistory","RelayHistoryTrigger","RelayHistoryContent","React","React","EMPTY_TARGETS","RECOVERY_SNAPSHOT_STALE_MS","useRecoverableCount","client","recipient","snapshot","setSnapshot","refreshNonce","setRefreshNonce","inFlightRef","lastAttemptRef","recipientKey","isAddress","cancelled","pending","promise","catalogError","targets","err","lookups","t","results","lookup","entries","i","recoveryError","result","refresh","value","refreshIfStale","lastAttempt","error","loading","badgeFailed","countRecoverableTokens","groupForwarders","import_react","RelayRecoveryContext","useRelayRecoveryContextValue","ctx","useRelayRecoveryContext","useRelayRecoverySnapshot","useRelayRecoveryCount","React","import_jsx_runtime","RelayRecoveryContent","className","props","useRelayRecoveryContext","open","useRelayShellContext","trackDappUiFunnel","TrackingBoundary","ShellContent","RelayRecoveryBody","recoveryTargets","recoveryLoading","refreshRecovery","useRelayRecoverySnapshot","theme","resolvedApiBaseUrl","resolveRelayApiBaseUrl","client","createRelayClient","wallet","connectedWallet","isAddress","recipientMatchesWallet","resolvedRecipient","resolvedEnv","resolveEnvironment","remoteTrusted","useRemoteConfigValue","config","resolveRelayForwarderFactories","effectiveTrustedFactories","mergeTrustedFactories","canSign","ready","recipientKey","orders","useRelayOrders","RecoverFunds","import_jsx_runtime","RelayRecoveryTrigger","asChild","children","recoverableCount","useRelayRecoveryCount","TrackingBoundary","ShellTrigger","import_jsx_runtime","RelayRecoveryRoot","props","theme","mobileBreakpoint","drawerDirection","dialogWidth","drawerMaxWidth","drawerMinWidth","style","openProp","onOpenChange","children","internalOpen","setInternalOpen","isControlled","open","handleOpenChange","value","client","baseUrl","resolveRelayApiBaseUrl","createRelayClient","connectedWallet","isAddress","recoveryRecipient","recovery","useRecoverableCount","reportedError","contextValue","RelayRecoveryContext","ResponsiveShell","RelayRecovery","RelayRecoveryTrigger","RelayRecoveryContent","import_react","useRelayConfig","opts","optsRef","client","createRelayClient","enabled","refetchInterval","config","setConfig","loading","setLoading","error","setError","refreshNonce","setRefreshNonce","cancelled","inFlight","refetchConfig","result","err","nextError","refetchTimer","refresh","value","import_react_query","import_react","nextClientId","clientIds","getClientId","client","existing","id","useRelayRecovery","opts","optsRef","createRelayClient","recipient","recipientKey","isAddress","target","queryOptions","clientId","queryKey","recoveryQuery","handledErrorAtRef","error","errorUpdatedAt","RelayApiError","refresh","actions","useRecoveryActions","info","recover","action","tokenAddress","import_jsx_runtime","EXPLORER_BASE_URL","FRIENDLY_TX_LABEL","stepGlyph","status","isTerminalFailure","stepperItemClassName","StatusTracker","orderId","order","error","isLoading","step","TxLink","orderTxs","txKey","hash","getOneUsdTokenKey","token","matchesGetOneUsdToken","ref","findGetOneUsdToken","tokens","resolveGetOneUsdRoute","pairs","action","isGetOneUsdTargetAvailable","available","target","parse","version","match","part","compareVersions","a","b","left","right","length","index","diff","meetsMinVersion","minVersion","currentVersion","DAPP_UI_VERSION","order","REMOTE_MODE_KEYS","isGetOneUsdActionVisible","action","modes","isGetOneUsdActionSupported","minVersions","meetsMinVersion","isGetOneUsdActionEnabled","ONE_USD_SITE_URLS","isOneUsdHostname","hostname","normalized","resolveOneUsdSiteUrl","config","env","id","type","React","useGetOneUsdApr","enabled","aprPercent","setAprPercent","alive","fetchCrossdAprPercent","value","hasBridgeLeg","pathType","action","formatTransactionAmount","value","integer","decimal","groupedInteger","trimmedDecimal","getChainLabel","chainId","getTransactionPendingLabel","info","getTransactionSuccessNote","amount","arriving","import_jsx_runtime","SwapActionIcon","props","BridgeActionIcon","TransferActionIcon","ChevronRightActionIcon","CloseActionIcon","styleInject","import_jsx_runtime","ACTIONS","SwapActionIcon","BridgeActionIcon","TransferActionIcon","DEFAULT_TRANSACTION_RESULT_DURATION_MS","errorMessage","error","callSafely","callback","value","useCollapsedTrigger","compactTrigger","collapseOnScroll","scrollThreshold","scrolled","setScrolled","threshold","collapseAt","expandAt","frame","collapsed","update","next","onScroll","RouteUnavailable","loading","GetOneUsd","walletAddress","tokens","pairs","bridge","relay","env","targetSymbolProp","relayTargetSymbol","targetIconUrl","mode","buttonComponent","theme","mobileBreakpoint","drawerDirection","dialogWidth","drawerMaxWidth","drawerMinWidth","style","className","contentClassName","hidden","waitForTransaction","transactionResultDurationMs","onRequestConnect","onEvent","chooserOpen","setChooserOpen","activeAction","setActiveAction","relayMounted","setRelayMounted","transactionStatus","setTransactionStatus","showLearnMore","setShowLearnMore","transactionIdRef","transactionLockedRef","resultTimerRef","mountedRef","iconOnly","resolvedEnv","resolveEnvironment","remoteModes","useRemoteConfigValue","config","remoteMinVersions","oneUsdSiteUrl","resolveOneUsdSiteUrl","ONE_USD_SITE_URLS","availableActions","id","isGetOneUsdActionVisible","hasEnabledActions","aprPercent","useGetOneUsdApr","swapRoute","resolveGetOneUsdRoute","bridgeRoute","targetToken","targetSymbol","iconUrl","ONEUSD_LOGO","triggerIconOnly","TriggerComponent","isOneUsdHostname","emit","event","openChooser","selectAction","action","isGetOneUsdActionEnabled","requestConnect","bridgeAction","route","routeReady","getToTokens","target","fromToken","available","isGetOneUsdTargetAvailable","onBridgeSubmitted","onBridgeSuccess","onBridgeFailed","bridgeProps","onRelayDeposited","onRelayError","relayProps","showTransactionResult","phase","label","txHash","handleBridgeSubmitted","info","pendingLabel","getTransactionPendingLabel","getTransactionSuccessNote","message","TrackingBoundary","ResponsiveShell","formatCrossdAprPercent","ShellContent","CloseActionIcon","InfoCircleIcon","eta","Icon","description","feeDelegated","supported","isGetOneUsdActionSupported","ChevronRightActionIcon","open","BridgeFlow","token","RelayDeposit","React","Dialog","import_vaul","import_react","WalletConnectModalContext","useWalletConnectModalContext","ctx","import_react","useWalletDetect","wallets","setWallets","isLoading","setIsLoading","detected","handleAnnouncement","event","info","w","timeout","isDetected","rdns","Dialog","import_vaul","import_jsx_runtime","WalletConnectModalTrigger","asChild","children","isMobile","useWalletConnectModalContext","Trigger","TrackingBoundary","import_react","import_crossx_design_system","Dialog","import_vaul","import_jsx_runtime","runConnectHandler","id","handler","trackDappUiFunnel","recordSelectedWallet","result","error","normalizeFailureReason","WalletItem","wallet","detected","onConnect","handleClick","Icon","featured","cn","SocialItem","social","onSignIn","label","ModalBody","wallets","activeWalletIds","socialProviders","activeSocialIds","termsUrl","privacyUrl","isDetected","close","isMobile","useWalletConnectModalContext","visibleIds","v","resolveWallet","hasSocial","hasWallets","CloseIcon","resolveSocial","TermsLink","href","track","children","WalletConnectModalContent","className","theme","portalStyle","open","TrackingBoundary","styleInject","styleInject","import_jsx_runtime","EMPTY_SOCIAL_PROVIDERS","WalletConnectModalRoot","wallets","socialProviders","termsUrl","privacyUrl","theme","mobileBreakpoint","drawerDirection","dialogWidth","drawerMaxWidth","drawerMinWidth","style","openProp","onOpenChange","children","isMobile","useMediaQuery","internalOpen","setInternalOpen","isControlled","open","handleOpenChange","value","close","useHistoryDismiss","isDetected","useWalletDetect","activeWalletIds","activeSocialIds","mergedStyle","sizeVars","contextValue","DEFAULT_TERMS_URL","DEFAULT_PRIVACY_URL","wrapper","WalletConnectModalContext","WalletConnectModal","WalletConnectModalTrigger","WalletConnectModalContent","import_crossx_design_system","React","styleInject","styleInject","CROSSX_SVG","METAMASK_SVG","BINANCE_SVG","GOOGLE_SVG","APPLE_SVG","toDataUri","svg","CROSSX_ICON","METAMASK_ICON","BINANCE_ICON","GOOGLE_ICON","APPLE_ICON","import_jsx_runtime","ConnectButton","isConnecting","address","provider","providerName","accountName","sendAccounts","onConnect","onDisconnect","onCopy","onSelectWallet","onBuy","onBuyDisabledMessage","label","connectingLabel","disconnectLabel","className","theme","env","showBalance","showPortfolio","drawerDirection","modal","connectorId","style","walletInfoStyle","sendTransaction","getTransactionReceipt","estimateGas","onReceive","onBridge","onSend","bridgeTokens","bridgeHistory","getBridgeQuote","getBridgeToTokens","getBridgeApproval","approveBridge","submitBridge","buttonClass","pillClass","setDappUiAnalyticsUser","resolvedProviderName","getDefaultProviderName","iconUrl","PROVIDER_ICON","TrackingBoundary","WalletInfo","PROVIDER_NAME","_","success","shorten","ConnectWalletIcon","GOOGLE_ICON","APPLE_ICON","CROSSX_ICON","METAMASK_ICON","BINANCE_ICON","React","styleInject","styleInject","import_jsx_runtime","DEFAULT_SKILLS_HREF","SkillsButton","label","href","onClick","className","style","theme","disabled","isLoading","loadingLabel","openInNewTab","type","loading","setLoading","env","resolveEnvironment","remoteHref","useRemoteConfigValue","config","resolveEnvValue","resolvedHref","handleClick","_e","isDisabled","showLoading","displayLabel","classes","TrackingBoundary","SparklesIcon","announceAppLauncherUsage"]}