@nexus-cross/dapp-ui 1.3.4 → 1.3.5-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +22 -21
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +22 -21
- package/dist/index.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lib/environment.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-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/lib/remote-config.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/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/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/components/sections/RewardsSection.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/queries/use-withdraw-positions.ts","../src/features/wallet-portfolio/queries/lp-balance-rpc-fallback.ts","../src/features/wallet-portfolio/components/sections/SectionCard.tsx","../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/components/sections/PointsSection.tsx","../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/types.ts","../src/features/wallet-portfolio/styles/portfolio.css","../src/features/bridge/components/BridgeFlow.tsx","../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/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/utilities.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/components/icons.tsx","../src/features/wallet-connect-modal/constants.ts","../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 { 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 { WalletPortfolio, WalletPortfolioBody, PORTFOLIO_SECTIONS } 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} 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 { 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 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","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","export const DAPP_UI_VERSION = '1.3.5';\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 1). Errors are never sampled. */\n analyticsSampleRate?: number;\n}\n\nlet dappUiScope: Scope | null = null;\nlet analyticsSampleRate = 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\nexport function isInAppBrowser(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.navigator.userAgent.includes(\"CROSSx\");\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 | \"send\"\n | \"skills\"\n | \"wallet_connect\"\n | \"wallet_info\"\n | \"wallet_portfolio\";\n\nexport type DappUiFlow = \"connect\" | \"send\" | \"bridge\" | \"withdraw\";\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 { 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 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 { 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, #123235);--al-badge-text: var(--ds-accent-primary-default, #00ceb2);--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, #e7f9f8);--al-badge-text: var(--ds-accent-primary-default, #00b4b7);--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 const DRAG_THRESHOLD = 8;\n const pointerStart = React.useRef<{ x: number; y: number } | null>(null);\n\n const handlePointerDown = React.useCallback((e: React.PointerEvent) => {\n pointerStart.current = { x: e.clientX, y: e.clientY };\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 if (pointerStart.current) {\n const dx = e.clientX - pointerStart.current.x;\n const dy = e.clientY - pointerStart.current.y;\n pointerStart.current = null;\n if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {\n return;\n }\n }\n\n const url = item.url[env] ?? item.url.production;\n const isExternal = isExternalLink(url, domain);\n\n if (!isExternal) {\n try {\n const urlPathname = new URL(url).pathname;\n const isActive =\n currentPathname === urlPathname ||\n currentPathname.startsWith(urlPathname + \"/\");\n if (isActive) {\n close();\n return;\n }\n } catch {}\n }\n\n if (isExternal) {\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n } else {\n window.location.href = url;\n }\n\n close();\n },\n [env, domain, close, currentPathname],\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 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 CROSS 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 ({ signal }) => {\n try {\n const res = await fetch(GLOBAL_MENU_URLS[resolvedEnv], {\n cache: \"no-store\",\n signal,\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch global menu: ${res.status}`);\n }\n return res.json();\n } catch (error) {\n if (signal.aborted) throw error;\n return getFallbackGlobalMenu(resolvedEnv);\n }\n },\n staleTime: 0,\n gcTime: 0,\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: \"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 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 { OnOutlink } from \"../../../lib/outlink\";\nimport { DEFAULT_TERMS_URL, DEFAULT_PRIVACY_URL } from \"../../../lib/constants\";\nimport { resolveEnvironment } from \"../../../lib/environment\";\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/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 /** 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 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 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\n React.useEffect(() => {\n if (!open) {\n setView(\"wallet\");\n setSelectedSendToken(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 showPortfolio,\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 showPortfolio,\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, 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\";\n\nexport type WalletInfoView = \"wallet\" | \"qr\" | \"portfolio\" | \"send\" | \"bridge\";\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 showPortfolio: boolean;\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 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: \"CROSSx\",\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}\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\nexport interface TokenInfoResponse {\n code: number;\n message: string;\n data: TokenInfo[];\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\nexport function QRLogoIcon({ size = 42, className }: IconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 42 42\"\n fill=\"none\"\n className={className}\n >\n <path\n d=\"M20.7314 2C31.0766 2 39.4628 10.3863 39.4629 20.7314C39.4629 31.0766 31.0766 39.4629 20.7314 39.4629C10.3863 39.4628 2 31.0766 2 20.7314C2.00007 10.3864 10.3864 2.00007 20.7314 2ZM20.5938 30.0586L21.1084 30.5732L21.623 30.0586V25.2471H21.1084C20.9344 25.2471 20.7626 25.235 20.5938 25.2158V30.0586ZM16.4834 24.6611L14.6514 26.4922L15.3799 27.2207L17.1797 25.4209L18.3193 24.2803C18.0487 24.0673 17.8025 23.8251 17.5869 23.5566L16.4834 24.6611ZM26.8691 27.2207L27.5967 26.4922L26.3516 25.2471H24.8955L26.8691 27.2207ZM11.2666 20.7314L11.7822 21.2471H16.624C16.6048 21.0781 16.5938 20.9065 16.5938 20.7324C16.5938 20.5585 16.6049 20.3866 16.624 20.2178H11.7812L11.2666 20.7314ZM20.5938 11.4053V16.2471C20.7626 16.2279 20.9345 16.2178 21.1084 16.2178H21.623V11.4053L21.1084 10.8906L20.5938 11.4053ZM14.6514 15.0029L16.4521 16.8027L17.5732 17.9238C17.7874 17.6546 18.0315 17.4104 18.3008 17.1963L15.3799 14.2754L14.6514 15.0029ZM25.0371 16.1074L24.9268 16.2178H26.3818L27.5957 15.0029L26.8682 14.2754L25.0371 16.1074Z\"\n fill=\"#019D92\"\n stroke=\"white\"\n strokeWidth={4}\n />\n </svg>\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\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 {\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 const nativeSymbol = chainInfo?.currency_symbol ?? \"\";\n const networkName = 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","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 * Bridge 약관 동의 UI 공통 단일 소스.)\n */\nexport const DEFAULT_TERMS_URL =\n \"https://terms.crosstoken.io/docs/cross-terms-of-use\";\nexport const DEFAULT_PRIVACY_URL =\n \"https://terms.crosstoken.io/docs/nexus-privacy-policy\";\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\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","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. \"CROSS\"). */\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 <circle cx=\"28\" cy=\"28\" r=\"28\" fill=\"var(--ds-accent-primary-default, #00b4b7)\" />\n <path\n d=\"M17 28.5 24.2 35.5 39 20.5\"\n stroke=\"var(--ds-accent-primary-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-primary-default, #00b4b7);color:var(--ds-accent-primary-on, #ffffff)}.sf-btn--primary:hover:not(:disabled):not([data-disabled]){background:var(--ds-accent-primary-high, #00a0a2)}.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-primary-default, #00b4b7);color:var(--ds-accent-primary-on, #ffffff);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-default, #00b4b7)}.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-default, #00b4b7)}.sf-address-field[data-filled]{border-color:var(--ds-accent-primary-default, #00b4b7)}.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, #00a0a2)}.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, #00a0a2)}.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-default, #00b4b7);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, #00a0a2)}.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 { 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 { useCrossdApr } 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 PortfolioActionIcon,\n ToastCheckIcon,\n} from \"./icons\";\nimport {\n shortenAddress,\n formatWei,\n formatPrice,\n formatTokenValue,\n} from \"../../../lib/format\";\nimport { cn } from \"../../../lib/utils\";\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 { 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(\"CROSSD\")) return 0;\n if (chainId === chains.cross && s === \"CROSS\") return 1;\n if (chainId === chains.bsc && s === \"CROSS\") 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\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 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 showPortfolio,\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 // 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 } = 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 = new BigNumber(token.quantity.numeric).shiftedBy(\n -token.quantity.decimals,\n );\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 = new BigNumber(a.quantity.numeric)\n .shiftedBy(-a.quantity.decimals)\n .multipliedBy(statA?.price ?? 0);\n const valueB = new BigNumber(b.quantity.numeric)\n .shiftedBy(-b.quantity.decimals)\n .multipliedBy(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 = new BigNumber(a.value);\n const valueB = new BigNumber(b.value);\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 // 브릿지 버튼 이동 대상 — 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 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 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 <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 {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 {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 >\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 </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={() => handleSelectSendToken(token)}\n aria-label={`Send ${token.symbol}`}\n data-track=\"token_send\"\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 {`APR ${crossdAprPercent >= 0 ? \"+\" : \"-\"}${(crossdAprPercent >= 0 ? Math.floor : Math.ceil)(Math.abs(crossdAprPercent) * 100) / 100}%`}\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={accountName}\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 // \"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 {accountName ?? \"Account 1\"}\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\">{accountName ?? \"Account 1\"}</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\">{accountName ?? \"Account 1\"}</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\" || view === \"bridge\"\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 </div>\n\n {view === \"wallet\" && 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 && view !== \"portfolio\" && view !== \"send\" && (\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 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 >\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 >\n {contentBody}\n </Popover.Content>\n </Popover.Portal>\n </TrackingBoundary>\n );\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\";\n\n/**\n * CROSSD 대시보드 overview API. prd 환경만 존재하므로 URL을 고정한다.\n * 응답의 `est_last_epoch_apy`는 비율값(1 == 100%, 0.05 == 5%).\n */\nconst 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\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: async ({ signal }) => {\n const res = await fetch(CROSSD_OVERVIEW_URL, {\n cache: \"no-store\",\n signal,\n });\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 return ratio * 100;\n },\n staleTime: 60_000,\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 { 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 return { categoryMap, ...rest };\n}\n\nexport { buildCategoryKey };\n","import { useMemo } from \"react\";\nimport BigNumber from \"bignumber.js\";\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 const amount = new BigNumber(t.quantity.numeric).shiftedBy(\n -t.quantity.decimals,\n );\n const value = amount.multipliedBy(new BigNumber(stat.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 type {\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 = \"terms\" | \"privacy\" | \"portfolio\" | \"send\";\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: \"send\";\n origin: \"send-transaction\";\n payload: {\n chainId: number;\n txHash: `0x${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 } 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 \"CROSS Rewards deposited assets\",\n \"CROSS Rewards claimable rewards\",\n ],\n excluded: [\n \"Forge LP value\",\n \"CROSS staking 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\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/** `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 {\n PORTFOLIO_SECTIONS,\n type EstimateGasFn,\n type LpBalanceReaderFn,\n type PortfolioSection,\n type SendTransactionFn,\n type StakingRewardsReaderFn,\n} from \"../types\";\nimport \"../styles/portfolio.css\";\n\nconst POLL_SEC = 15;\nconst WP_QUERY_PREFIX = \"wp:\";\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 * 컨테이너 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 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 queryClient.invalidateQueries({\n predicate: (query) =>\n typeof query.queryKey[0] === \"string\" &&\n query.queryKey[0].startsWith(WP_QUERY_PREFIX),\n });\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) 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, 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 {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 CROSS 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","\"use client\";\n\nimport * as React from \"react\";\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 { useWithdrawPositions } from \"../../queries/use-withdraw-positions\";\nimport { APP_LAUNCHER_ICONS_BASE, REWARDS_WEB } from \"../../constants\";\nimport type { RewardPool } from \"../../types\";\nimport { formatPortfolioTokenAmount, formatPortfolioUsd } 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\";\nimport BigNumber from \"bignumber.js\";\n\nconst SECTION_TITLE = \"CROSS Rewards\";\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 const flow = useWithdrawFlow(\"rewards\");\n const { positions } = useWithdrawPositions(\n env,\n walletAddress,\n hasAddress && flow.canWithdrawInline,\n );\n\n // 이 섹션의 다이얼로그 토큰 리스트: CROSS 예치(CrossPool) 포지션.\n // 게임 토큰 예치는 Points 섹션 소관.\n const dialogPositions = React.useMemo<WithdrawDialogPosition[]>(\n () =>\n positions\n .filter((p) => p.poolType === \"CrossPool\")\n .map((p) => ({\n ...p,\n icon: getTokenImage(p.tokenAddress, p.tokenSymbol),\n })),\n [positions, getTokenImage],\n );\n\n const getRewardsUrl = (pool: RewardPool, action: \"withdraw\" | \"claim\") =>\n `${REWARDS_WEB[env]}/rewards/${pool.pool_address}?action=${action}`;\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 <>\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 const canWithdraw = !!userDeposit && isPositiveRawAmount(deposited);\n const inlineWithdrawReady =\n flow.canWithdrawInline &&\n dialogPositions.some(\n (p) =>\n p.poolAddress.toLowerCase() ===\n pool.pool_address.toLowerCase(),\n );\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 {inlineWithdrawReady ? (\n <button\n type=\"button\"\n className=\"wp-tag wp-tag--muted\"\n data-disabled={!canWithdraw || undefined}\n aria-disabled={!canWithdraw || undefined}\n data-track=\"rewards_withdraw_open\"\n onClick={() => {\n if (!canWithdraw) return;\n flow.openWithdraw(pool.pool_address);\n }}\n >\n <span className=\"wp-tag-label\">Withdraw</span>\n </button>\n ) : (\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 )}\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 <WithdrawDialog\n open={flow.isOpen}\n status={flow.status}\n positions={dialogPositions}\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 />\n </>\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\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 const getTokenImage = (address: string, symbol?: string): string => {\n const addr = address.toLowerCase();\n\n const byChain = byChainAddr.get(`${crossChainId}:${addr}`);\n if (byChain?.image) return byChain.image;\n\n const byAddr = byChainAddr.get(addr);\n if (byAddr?.image) return byAddr.image;\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 const bySym = byChainSym.get(sym);\n if (bySym?.image) return bySym.image;\n }\n\n return \"\";\n };\n\n return { getTokenImage, ...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","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): Promise<string | undefined> {\n try {\n return await rpcCall<string>(rpcUrl, \"eth_call\", [{ to, data }, \"latest\"], signal);\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 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 * 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 shortenTransactionHash,\n} from \"../../utils\";\nimport {\n formatRawTokenAmount,\n parseTokenAmount,\n} from \"../../../send/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}\n\n/** 입력창에서 허용하는 소수 자릿수 (표기 규칙과 동일하게 2자리). */\nconst INPUT_RE = /^\\d*\\.?\\d{0,2}$/;\nconst QUICK_ADD_AMOUNTS = [100n, 1000n, 2000n] as const;\nconst GAS_ESTIMATE_DEBOUNCE_MS = 400;\n\nfunction formatFeeValue(\n estimate: GasEstimate | null,\n status: WithdrawGasEstimateStatus,\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)} CROSS`;\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}: WithdrawDialogProps) {\n const [selectedPoolAddress, setSelectedPoolAddress] = React.useState<\n string | undefined\n >(undefined);\n const [amountInput, setAmountInput] = React.useState(\"\");\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 const selected = React.useMemo(() => {\n const key = (selectedPoolAddress ?? initialPoolAddress)?.toLowerCase();\n return (\n positions.find((p) => p.poolAddress.toLowerCase() === key) ??\n positions[0] ??\n null\n );\n }, [positions, selectedPoolAddress, 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 // 입력 문자열 → raw(wei). 유효하지 않으면 null.\n const amountRaw = React.useMemo(() => {\n const parsed = parseTokenAmount(amountInput, decimals);\n return \"value\" in parsed ? parsed.value : null;\n }, [amountInput, decimals]);\n\n const exceedsBalance = amountRaw !== null && amountRaw > depositedRaw;\n\n React.useEffect(() => {\n if (!open || !selected) return;\n trackFunnel?.(\"position_selected\");\n }, [open, selected, trackFunnel]);\n\n React.useEffect(() => {\n if (!open || amountRaw === null || amountRaw <= 0n || exceedsBalance) {\n return;\n }\n trackFunnel?.(\"amount_entered\");\n }, [open, amountRaw, exceedsBalance, trackFunnel]);\n\n const canSubmit =\n status === \"confirm\" &&\n !!selected &&\n amountRaw !== null &&\n amountRaw > 0n &&\n !exceedsBalance;\n\n const resetAmountAndGas = React.useCallback(() => {\n setAmountInput(\"\");\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 setSelectedPoolAddress(initialPoolAddress);\n resetAmountAndGas();\n // initialPoolAddress는 열리는 순간의 값만 반영한다.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n const handleSelect = React.useCallback(\n (position: WithdrawDialogPosition) => {\n if (status !== \"confirm\") return;\n setSelectedPoolAddress(position.poolAddress);\n resetAmountAndGas();\n },\n [status, resetAmountAndGas],\n );\n\n // 유효 금액이 잡히면 디바운스 후 가스 추정. 같은 풀·금액 조합은 한 번만\n // 추정한다(2초 폴링이 selected 참조만 바꿔도 재추정하지 않도록).\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 (amountRaw === null || amountRaw <= 0n || exceedsBalance) {\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()}:${amountRaw.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, amountRaw)\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, amountRaw, exceedsBalance, estimateWithdraw, selected]);\n\n const handleInputChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n const next = event.target.value.replace(/,/g, \"\");\n if (next === \"\" || INPUT_RE.test(next)) {\n setAmountInput(next);\n }\n },\n [],\n );\n\n const setAmountFromRaw = React.useCallback(\n (raw: bigint) => {\n setAmountInput(formatRawTokenAmount(raw.toString(), decimals));\n },\n [decimals],\n );\n\n const handleQuickAdd = React.useCallback(\n (add: bigint) => {\n const unit = 10n ** BigInt(decimals);\n const current = amountRaw ?? 0n;\n const next = current + add * unit;\n setAmountFromRaw(next > depositedRaw ? depositedRaw : next);\n },\n [amountRaw, decimals, depositedRaw, setAmountFromRaw],\n );\n\n const handleMax = React.useCallback(() => {\n setAmountFromRaw(depositedRaw);\n }, [depositedRaw, setAmountFromRaw]);\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 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 <div className=\"wp-claim-heading\">\n <h3 id=\"wp-withdraw-title\" className=\"wp-claim-title\">\n {status === \"error\" ? \"Withdraw Failed\" : \"Withdraw\"}\n </h3>\n {status === \"error\" && (\n <p className=\"wp-claim-error-message\">\n {error || \"The withdraw request could not be completed.\"}\n </p>\n )}\n </div>\n\n {positions.length > 1 && (\n <div\n className=\"wp-withdraw-token-list\"\n role=\"listbox\"\n aria-label=\"Withdrawable tokens\"\n >\n {positions.map((position) => {\n const isSelected =\n position.poolAddress.toLowerCase() ===\n selected.poolAddress.toLowerCase();\n return (\n <button\n key={position.poolAddress}\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n className=\"wp-withdraw-token\"\n data-selected={isSelected || undefined}\n data-track=\"withdraw_position_select\"\n data-track-id={position.tokenSymbol}\n disabled={isSubmitting}\n onClick={() => handleSelect(position)}\n >\n <span className=\"wp-withdraw-token-icon\" aria-hidden>\n <TokenBadge position={position} size={28} />\n </span>\n <span className=\"wp-withdraw-token-symbol\">\n {position.tokenSymbol}\n </span>\n <span className=\"wp-withdraw-token-amount\">\n {formatPortfolioTokenAmount(\n position.depositedRaw,\n position.decimals,\n )}\n </span>\n </button>\n );\n })}\n </div>\n )}\n\n <div\n className=\"wp-claim-reward-card\"\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 className=\"wp-withdraw-amount-card\">\n <div className=\"wp-withdraw-amount-row\">\n <input\n className=\"wp-withdraw-input\"\n type=\"text\"\n inputMode=\"decimal\"\n autoComplete=\"off\"\n placeholder=\"0\"\n value={amountInput}\n disabled={isSubmitting}\n onChange={handleInputChange}\n aria-label={`Withdraw amount (${selected.tokenSymbol})`}\n aria-invalid={exceedsBalance || undefined}\n />\n <span className=\"wp-withdraw-input-symbol\">\n {selected.tokenSymbol}\n </span>\n </div>\n {exceedsBalance && (\n <p className=\"wp-claim-error-message\">\n Amount exceeds deposited balance.\n </p>\n )}\n <div\n className=\"wp-withdraw-quick-row\"\n role=\"group\"\n aria-label=\"Quick amounts\"\n >\n {QUICK_ADD_AMOUNTS.map((add) => (\n <button\n key={add.toString()}\n type=\"button\"\n className=\"wp-withdraw-quick\"\n data-track=\"withdraw_quick_add\"\n disabled={isSubmitting}\n onClick={() => handleQuickAdd(add)}\n >\n +{add.toString()}\n </button>\n ))}\n <button\n type=\"button\"\n className=\"wp-withdraw-quick\"\n data-track=\"withdraw_max\"\n disabled={isSubmitting}\n onClick={handleMax}\n >\n MAX\n </button>\n </div>\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>{formatFeeValue(gasEstimate, gasStatus)}</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 (\n selected &&\n amountRaw !== null &&\n amountRaw > 0n &&\n !exceedsBalance\n ) {\n onConfirm(selected, amountRaw, gasEstimate);\n }\n }}\n disabled={\n isSubmitting ||\n ((status === \"confirm\" || status === \"error\") &&\n (amountRaw === null || amountRaw <= 0n || exceedsBalance))\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 } from \"../../utils\";\nimport {\n normalizeFailureReason,\n trackDappUiFunnel,\n type TrackDappUiFunnelOptions,\n} from \"../../../../lib/sentry/index\";\nimport type {\n WithdrawDialogPosition,\n WithdrawDialogStatus,\n} from \"./WithdrawDialog\";\n\nconst WP_QUERY_PREFIX = \"wp:\";\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 queryClient.invalidateQueries({\n predicate: (query) =>\n typeof query.queryKey[0] === \"string\" &&\n query.queryKey[0].startsWith(WP_QUERY_PREFIX),\n });\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(\n err instanceof Error ? err.message : \"Failed to request withdraw.\",\n );\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","\"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 { formatPortfolioTokenAmount, formatPortfolioUsd } 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 = \"Points\";\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 } = useTokenInfo(env, hasAddress);\n const flow = useWithdrawFlow(\"points\");\n const { positions, isLoading: positionsLoading } = useWithdrawPositions(\n env,\n walletAddress,\n hasAddress,\n );\n\n // My-Point 예치 퀘스트 대상은 게임 토큰 풀. CROSS 예치(CrossPool)는\n // CROSS Rewards 섹션 소관이므로 여기서는 제외한다.\n const pointPositions = React.useMemo<WithdrawDialogPosition[]>(\n () =>\n positions\n .filter((p) => p.poolType !== \"CrossPool\")\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 />\n </>\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 { 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\nconst SECTION_TITLE = \"CROSS Staking\";\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 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={SECTION_TITLE}\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)} CROSS\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)} CROSS\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() === \"CROSS\";\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 { 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() === \"CROSS\";\n}\n\n/**\n * 백엔드가 CROSS 쪽 token ref를 빈 값(symbol \"\", decimals 0)으로 내려주므로,\n * 0/미지정 decimals는 unset으로 보고 18(CROSS 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): PoolRow {\n const tokenASymbol = pool.token_a?.symbol?.trim() || \"TOKEN\";\n const tokenBSymbol = pool.token_b?.symbol?.trim() || \"CROSS\";\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)} CROSS`;\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 // 전용 game-swap 아이콘이 버킷에 확인되지 않아, 존재가 검증된 dex.png를 사용한다.\n // 전용 에셋이 추가되면 파일명만 교체하면 된다.\n const sectionIcon = `${APP_LAUNCHER_ICONS_BASE[env]}/dex.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(pool, balances[pool.pair_address?.toLowerCase()], fiat),\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]);\n\n const getManageUrl = (pool: GameSwapPool) => {\n const tokenAddress = pool.token_a?.address ?? pool.pair_address;\n return `${GAME_SWAP_WEB[env]}/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 = \"Forge\";\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","/**\n * 포트폴리오 본문에 노출 가능한 섹션 종류.\n * - `\"rewards\"` : CROSS Rewards\n * - `\"points\"` : CROSS Points (게임 토큰 예치 퀘스트, 내장 Withdraw)\n * - `\"staking\"` : CROSS Staking\n * - `\"gameSwap\"` : Gametoken LP\n * - `\"forge\"` : Forge\n *\n * `WalletPortfolioBody` / `WalletInfo`의 섹션 필터에 사용한다.\n */\nexport type PortfolioSection =\n | \"rewards\"\n | \"points\"\n | \"staking\"\n | \"gameSwap\"\n | \"forge\";\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];\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은 \"CROSS\"로, 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","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-default, #00a0a2);--wp-green: var(--ds-accent-primary-default, #00a0a2);--wp-accent-soft: var(--ds-accent-primary-low, rgba(0, 160, 162, .14));--wp-red: var(--ds-system-red-default, #e70077);--wp-blue: #0488fc;--wp-purple: #7346f3;--wp-orange: #ff9d00;--wp-claim-primary: #9b8cef;--wp-claim-primary-text: #202530;--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-default, #00a0a2);--wp-green: var(--ds-accent-primary-default, #00a0a2);--wp-accent-soft: var(--ds-accent-primary-low, #e7f9f8);--wp-red: var(--ds-system-red-default, #e70077);--wp-blue: #0488fc;--wp-purple: #7346f3;--wp-orange: #ff9d00;--wp-claim-primary: #7346f3;--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-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-width:375px;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}}.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:8px;width:100%;min-height:60px;padding:16px;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-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 { cn } from \"../../../lib/utils\";\nimport { TrackingBoundary } from \"../../../lib/sentry\";\nimport {\n resolveEnvValue,\n useRemoteConfigValue,\n type AppsConfig,\n} from \"../../../lib/remote-config\";\nimport { BackIcon, ChevronDownIcon, CloseIcon } from \"../../wallet-info/components/icons\";\nimport {\n ArrowDownIcon,\n ArrowRightIcon,\n ArrowRightLeftIcon,\n HistoryIcon,\n SearchIcon,\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 className?: string;\n}\n\ntype TokenPickerSide = \"from\" | \"to\";\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 = new BigNumber(amount || \"0\");\n const balance = new BigNumber(token.balance || \"0\");\n if (!value.isFinite() || !balance.isFinite()) return false;\n return value.isGreaterThan(balance);\n}\n\nfunction formatBalanceUsd(token: BridgeToken): string | null {\n if (token.priceUsd === undefined) return null;\n const balance = new BigNumber(token.balance || \"0\");\n if (!balance.isFinite()) return null;\n return `≈ $${balance.multipliedBy(token.priceUsd).toFormat(2)} USD`;\n}\n\nfunction formatTokenPriceUsd(token: BridgeToken): string | null {\n if (token.priceUsd === undefined) return null;\n const price = new BigNumber(token.priceUsd);\n if (!price.isFinite()) return null;\n return `$${price.toFormat(price.isLessThan(1) ? 4 : 2)}`;\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 = new BigNumber(value || \"0\");\n if (!amount.isFinite() || amount.isNaN()) 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 value = new BigNumber(amount || \"0\").multipliedBy(token.priceUsd);\n if (!value.isFinite() || value.isNaN()) 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 === 5555) return \"CROSS\";\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\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 />\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_select_open\"}\n data-track-id={label.toLowerCase()}\n aria-label={`Select ${label.toLowerCase()} token`}\n >\n <TokenIcon token={token} />\n <span>{token?.symbol ?? \"Select\"}</span>\n {!locked && <ChevronDownIcon size={16} />}\n </button>\n </div>\n </section>\n );\n}\n\nfunction BridgeTokenPicker({\n flow,\n side,\n onClose,\n}: {\n flow: BridgeFlowState;\n side: TokenPickerSide;\n onClose: () => void;\n}) {\n const titleId = React.useId();\n const [query, setQuery] = React.useState(\"\");\n const [portalTarget, setPortalTarget] = React.useState<HTMLElement | null>(null);\n const theme = useThemeAttr();\n const selectedToken = side === \"from\" ? flow.fromToken : flow.toToken;\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 const selectableTokens = React.useMemo(() => {\n if (side === \"from\") return flow.tokens;\n return flow.availableToTokens;\n }, [flow.availableToTokens, flow.tokens, side]);\n\n const filteredTokens = React.useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return selectableTokens;\n return selectableTokens.filter(\n (token) =>\n token.symbol.toLowerCase().includes(q) ||\n token.name.toLowerCase().includes(q),\n );\n }, [query, selectableTokens]);\n\n const handleSelect = React.useCallback(\n (token: BridgeToken) => {\n if (side === \"from\") {\n flow.selectFromToken(token);\n } else {\n flow.selectToToken(token);\n }\n onClose();\n },\n [flow, onClose, side],\n );\n\n if (!portalTarget) return null;\n\n return createPortal(\n <div\n className=\"bf-token-picker-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-token-picker\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n >\n <span className=\"bf-token-picker-handle\" aria-hidden=\"true\" />\n <header className=\"bf-token-picker-head\">\n <h3 id={titleId}>Select a token</h3>\n <button\n type=\"button\"\n className=\"bf-token-picker-close\"\n onClick={onClose}\n aria-label=\"Close token selector\"\n >\n <CloseIcon size={24} />\n </button>\n </header>\n\n <label className=\"bf-token-search\">\n <SearchIcon size={20} />\n <span className=\"bf-sr-only\">Search tokens</span>\n <input\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search Tokens\"\n autoComplete=\"off\"\n spellCheck={false}\n />\n </label>\n\n <div className=\"bf-token-list\">\n {filteredTokens.map((token) => {\n const isSelected = sameBridgeAsset(token, selectedToken);\n const usd = isSelected\n ? formatTokenPriceUsd(token)\n : formatBalanceUsd(token);\n return (\n <button\n key={tokenKey(token)}\n type=\"button\"\n className=\"bf-token-row\"\n data-selected={isSelected ? \"true\" : \"false\"}\n onClick={() => handleSelect(token)}\n >\n <span className=\"bf-token-row-left\">\n <TokenIcon token={token} size={40} />\n <span className=\"bf-token-meta\">\n <span className=\"bf-token-symbol\">{token.symbol}</span>\n {!isSelected && (\n <span className=\"bf-token-name\">{token.name}</span>\n )}\n </span>\n </span>\n <span className=\"bf-token-row-right\">\n <span className=\"bf-token-balance\">\n {formatBalance(token.balance)}\n {!isSelected ? ` ${token.symbol}` : null}\n </span>\n {usd && <span className=\"bf-token-price\">{usd}</span>}\n </span>\n </button>\n );\n })}\n {filteredTokens.length === 0 && (\n <div className=\"bf-token-empty\">No tokens found</div>\n )}\n </div>\n </section>\n </div>,\n portalTarget,\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({ flow }: { flow: BridgeFlowState }) {\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\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 if (!portalTarget || !flow.termsOpen) return null;\n\n return createPortal(\n <div\n className=\"bf-action-modal-overlay\"\n data-theme={theme}\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 CROSSD.\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 <span className=\"bf-terms-label\">Agree to Terms of Use</span>\n {flow.termsUrl && (\n <a\n href={flow.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 CROSSD\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,\n}: {\n flow: BridgeFlowState;\n approval: BridgeApprovalInfo;\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 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]);\n\n if (!portalTarget) return null;\n\n const disabled = flow.status === \"submitting\" || !flow.approveBridge;\n const isApproving = flow.status === \"submitting\";\n\n return createPortal(\n <div className=\"bf-action-modal-overlay\" data-theme={theme} role=\"presentation\">\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 if (!portalTarget || !flow.approvalSuccessOpen) return null;\n const isContinuing = flow.status === \"submitting\";\n\n return createPortal(\n <div className=\"bf-action-modal-overlay\" data-theme={theme} role=\"presentation\">\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 [isRateReversed, setIsRateReversed] = React.useState(false);\n\n const exchangeRate = React.useMemo(() => {\n const from = new BigNumber(fromAmount || \"0\");\n const to = new BigNumber(toAmount || \"0\");\n if (!from.isFinite() || !to.isFinite() || from.lte(0) || to.lte(0)) {\n return null;\n }\n return isRateReversed ? from.dividedBy(to) : to.dividedBy(from);\n }, [fromAmount, isRateReversed, toAmount]);\n\n if (!exchangeRate || !fromToken || !toToken) return null;\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 if (fromToken.symbol === toToken.symbol) return null;\n\n const fromSymbol = isRateReversed ? toToken.symbol : fromToken.symbol;\n const toSymbol = isRateReversed ? fromToken.symbol : toToken.symbol;\n\n return (\n <button\n type=\"button\"\n className=\"bf-exchange-rate\"\n onClick={() => setIsRateReversed((value) => !value)}\n data-track=\"rate_reverse\"\n >\n <span>1 {fromSymbol}</span>\n <ArrowRightLeftIcon size={12} />\n <span>{formatRateDecimal(exchangeRate)} {toSymbol}</span>\n </button>\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, index) => (\n <div className=\"bf-info-row\" key={`${row.label}-${row.value}`}>\n <span className=\"bf-info-label\">\n <span className=\"bf-tree-marker\">{index === rows.length - 1 ? \"└\" : \"├\"}</span>\n <span>{row.label}</span>\n </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\nfunction OrderedInfoSections({\n summary,\n}: {\n summary: BridgeTxSummary;\n}) {\n const hasBridgeInfo = Boolean(summary.bridgeInfo?.length);\n const hasSwapInfo = Boolean(summary.swapInfo?.length);\n const showNestedTitles = hasBridgeInfo && hasSwapInfo;\n const sections =\n summary.pathType === \"bridge-swap\"\n ? ([\"bridge\", \"swap\"] as const)\n : ([\"swap\", \"bridge\"] as const);\n\n return (\n <>\n {sections.map((section) => {\n const rows = section === \"swap\" ? summary.swapInfo : summary.bridgeInfo;\n if (!rows?.length) return null;\n\n return (\n <div className=\"bf-info-section\" key={section}>\n {showNestedTitles && (\n <h4>{section === \"swap\" ? \"Swap Info\" : \"Bridge Info\"}</h4>\n )}\n <InfoRows rows={rows} />\n </div>\n );\n })}\n </>\n );\n}\n\nfunction BridgeDetails({ summary }: { summary: BridgeTxSummary }) {\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 <OrderedInfoSections summary={summary} />\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 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 FormView({\n flow,\n env,\n}: {\n flow: BridgeFlowState;\n env: Environment;\n}) {\n const [tokenPickerSide, setTokenPickerSide] =\n React.useState<TokenPickerSide | null>(null);\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 const summary = hasAmount ? flow.summary ?? defaultSummary(flow) : null;\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={() => setTokenPickerSide(\"from\")}\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={() => setTokenPickerSide(\"to\")}\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 {summary && (\n <BridgeDetails summary={summary} />\n )}\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 {tokenPickerSide && (\n <BridgeTokenPicker\n flow={flow}\n side={tokenPickerSide}\n onClose={() => setTokenPickerSide(null)}\n />\n )}\n {slippageSettingsOpen && (\n <BridgeSlippageSettings\n flow={flow}\n onClose={() => setSlippageSettingsOpen(false)}\n />\n )}\n <BridgeTermsModal flow={flow} />\n {flow.approvalInfo && (\n <BridgeApproveModal flow={flow} approval={flow.approvalInfo} />\n )}\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 env,\n className,\n ...rest\n}: BridgeFlowProps) {\n const resolvedEnv = React.useMemo(() => resolveEnvironment(env), [env]);\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\" : \"Swap & Bridge\";\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 >\n <header className=\"bf-header\">\n <button type=\"button\" className=\"bf-header-btn\" onClick={flow.back} aria-label=\"Back\">\n <BackIcon size={24} />\n </button>\n <span className=\"bf-header-title\">{title}</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 ) : (\n <FormView flow={flow} env={resolvedEnv} />\n )}\n </div>\n </ThemeContext.Provider>\n </TrackingBoundary>\n );\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\nexport function ArrowRightLeftIcon({ 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=\"M3.5 2 1.5 4l2 2M1.5 4h9M8.5 10l2-2-2-2M10.5 8h-9\"\n stroke=\"currentColor\"\n strokeWidth={1.2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\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 { DEFAULT_TERMS_URL } from \"../../../lib/constants\";\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 CROSS-family symbol, dropping the testnet \"t\" prefix\n// (e.g. \"tCROSSD\" → \"CROSSD\", \"tCROSS\" → \"CROSS\").\nfunction normalizeCrossSymbol(symbol: string): string {\n const s = symbol.toUpperCase();\n return s.startsWith(\"T\") && s.slice(1).startsWith(\"CROSS\") ? s.slice(1) : s;\n}\n\n// The CROSS↔CROSSD 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 (CROSSD 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(\"CROSS\") && symbols.has(\"CROSSD\");\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 // 미설정 시 dapp-ui 공통 약관 문서로 폴백해 Terms 링크가 항상 노출되게 한다.\n termsUrl = DEFAULT_TERMS_URL,\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 (CROSS↔CROSSD) 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 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 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 }\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 status === \"submitting\" ||\n !approvalInfo ||\n !pendingSubmitInput ||\n !approveBridge\n ) {\n return;\n }\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 }\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\";\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 const amount = new BigNumber(sourceAmount || \"0\");\n\n if (!amount.isFinite() || amount.isNaN() || 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: #1e232e;--bf-toast-color: rgba(255, 255, 255, .85);position:relative;display:flex;flex:1;min-height:0;flex-direction:column;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))}.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-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-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-weight:600;line-height:1.5;letter-spacing:-.01em;outline:none}.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}.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, #00ceb2);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-picker-overlay{--bf-bg: #1e232e;--bf-surface: rgba(255, 255, 255, .05);--bf-surface-hover: rgba(255, 255, 255, .08);--bf-border: rgba(255, 255, 255, .07);--bf-text-high: rgba(255, 255, 255, .85);--bf-text-medium: rgba(255, 255, 255, .65);--bf-text-low: rgba(255, 255, 255, .4);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-token-picker-overlay[data-theme=light]{--bf-bg: #ffffff;--bf-surface: rgba(18, 18, 18, .05);--bf-surface-hover: rgba(18, 18, 18, .08);--bf-border: rgba(18, 18, 18, .07);--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-token-picker{display:flex;width:min(480px,calc(100vw - 48px));height:min(466px,calc(100dvh - 48px));max-height:calc(100dvh - 48px);min-height:0;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 #0006;box-sizing:border-box}.bf-token-picker-handle{display:none}.bf-token-picker-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(--bf-border);box-sizing:border-box}.bf-token-picker-head h3{margin:0;color:var(--bf-text-high);font-size:20px;font-weight:600;line-height:1.25;letter-spacing:0}.bf-token-picker-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(--bf-text-medium);cursor:pointer}.bf-token-picker-close:hover{color:var(--bf-text-high)}.bf-token-search{display:flex;min-height:48px;flex-shrink:0;align-items:center;gap:8px;margin:16px 24px;padding:12px 16px;border:1px solid transparent;border-radius:12px;background:var(--ds-surface-medium, rgba(18, 18, 18, .05));color:var(--bf-text-low);box-sizing:border-box}.bf-token-search:focus-within{border-color:var(--ds-accent-primary-default, #00ceb2)}.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, #00b4b7);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, #00b4b7);background:var(--ds-accent-primary-low, #e7f9f8)}.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, #00b4b7)}.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, #00b4b7);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}.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}.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:center;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-check-row{display:grid;width:100%;min-height:56px;grid-template-columns:20px minmax(0,1fr);align-items:center;column-gap:12px;padding:14px 16px;border:none;border-radius:12px;background:var(--bf-surface);color:var(--bf-text-medium);font-size:16px;font-weight:700;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}.bf-terms-check-button:focus-visible .bf-terms-checkbox{outline:2px solid var(--ds-accent-primary-default, #00ceb2);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:5px;background:transparent;box-sizing:border-box}.bf-terms-checkbox[data-checked=true]{border-color:var(--ds-accent-primary-default, #00ceb2);background:var(--ds-accent-primary-default, #00ceb2)}.bf-terms-checkbox[data-checked=true]:after{width:8px;height:4px;border:solid var(--ds-accent-primary-on, #000000);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, #00ceb2);font-size:14px;font-weight:700;text-decoration:none;white-space:nowrap}.bf-terms-privacy{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-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, #00ceb2);color:var(--ds-accent-primary-on, #000000)}.bf-action-primary:disabled{cursor:not-allowed;opacity:.35}.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, #00ceb2);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-radius:12px;background:var(--bf-toast-bg, #1e232e);color:var(--bf-toast-color, rgba(255, 255, 255, .85));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, #00ceb2);border-right-color:var(--ds-accent-primary-default, #00ceb2);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-accent-primary-default, #00ceb2)}.bf-status-check:after{position:absolute;top:6px;left:5px;width:8px;height:4px;border:solid var(--ds-accent-primary-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:#fff;font-size:14px;font-weight:600;line-height:1.5;letter-spacing:-.01em}.bf-status-toast-copy span{color:var(--bf-toast-color, rgba(255, 255, 255, .85));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-accent-primary-on-inverted, #00e8c9)}.bf-status-toast[data-variant=error]{border:1px solid rgba(255,43,95,.28);background:var(--bf-toast-bg, #1e232e)}.bf-status-toast[data-variant=error] .bf-status-toast-copy strong{color:#fff}.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:#ffffffe0}.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, #00ceb2);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-exchange-rate{display:flex;width:100%;align-items:center;justify-content:center;gap:4px;padding:0 0 24px;border:0;background:transparent;color:var(--bf-text-low);font:inherit;font-size:12px;font-weight:500;line-height:1.5;letter-spacing:-.01em;cursor:pointer}.bf-exchange-rate:hover{color:var(--bf-text-medium)}.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-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-tree-marker{width:10px;flex-shrink:0;color:var(--bf-text-low)}.bf-info-value{min-width:0;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{color:var(--bf-text-medium)}.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, #00a0a2)}.bf-info-value[data-tone=warning]{color:var(--ds-system-orange-high, #e56f00)}.bf-root[data-view=history] .bf-info-label,.bf-root[data-view=history] .bf-liquidity-head,.bf-root[data-view=history] .bf-tree-marker{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, #00a0a2)}.bf-root[data-view=history] .bf-info-value[data-tone=warning]{color:var(--ds-system-orange-high, #e56f00)}.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, #00ceb2)}.bf-liquidity-status[data-status=normal]:before,.bf-liquidity-status:not([data-status]):before{background:var(--ds-accent-primary-default, #00ceb2)}.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, #00ceb2)}.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, #00ceb2);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, #00ceb2);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, #00a0a2)}.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, #00a0a2);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-token-picker-overlay{align-items:flex-end;padding:0}.bf-token-picker{width:100%;max-height:82dvh;min-height:min(520px,82dvh);border-right:0;border-bottom:0;border-left:0;border-radius:24px 24px 0 0}.bf-token-picker-handle{display:block;width:40px;height:4px;flex-shrink:0;align-self:center;margin:10px 0 0;border-radius:999px;background:#ffffff38}.bf-token-picker-head{min-height:55px;padding:12px 56px 16px 24px;border-bottom:0}.bf-token-picker-close{top:13px}.bf-token-search{margin:0 16px 12px}.bf-token-list{padding:0 16px 16px}.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}\\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-default, #019d92);--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-default, #019d92);--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:600px;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(600px + 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, #00d5aa);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-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:36px;color:var(--ds-content-highest, #000000)}.wi-total-lte{margin-right:2px;font-weight:500;font-size:22px;color:var(--wi-texticon-secondary)}.wi-total-mask{letter-spacing:2px;font-size:32px;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-default, #00b4b7)}.wi-send-toast-spinner{width:18px;height:18px;border:2px solid rgba(0,232,201,.22);border-top-color:var(--ds-accent-primary-on-inverted, #00e8c9);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, #00e8c9)}.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-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 * as Dialog from \"@radix-ui/react-dialog\";\nimport { Drawer } from \"vaul\";\nimport { WalletConnectModalContext } from \"../providers/wallet-connect-modal-provider\";\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 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 * 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 { 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 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\">CROSS Wallet</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","export function CROSSxIcon() {\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 <circle cx=\"20\" cy=\"20\" r=\"20\" fill=\"#00D5AA\" />\n <path\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n d=\"M20.3225 2.85742L23.7249 6.25982V11.8301L28.1175 7.43757L32.9292 12.2493L28.5806 16.5979H20.3225C18.4435 16.5979 16.9201 18.1212 16.9201 20.0003C16.9201 21.8794 18.4434 23.4027 20.3225 23.4027H28.5372L32.9293 27.7948L28.1176 32.6066L23.7249 28.2139V33.7407L20.3225 37.1431L16.9201 33.7407V28.2584L12.5719 32.6066L7.76022 27.7948L12.1524 23.4027H6.58209L3.17969 20.0003L6.58209 16.5979H12.1089L7.76035 12.2493L12.5721 7.43757L16.9201 11.7857V6.25982L20.3225 2.85742Z\"\n fill=\"black\"\n />\n </svg>\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 {\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: \"CROSSx with Social\",\n description: \"Sign in with Google or Apple\",\n icon: CROSSxIcon,\n },\n cross_wallet: {\n id: \"cross_wallet\",\n name: \"CROSSx\",\n description: \"App approval is required\",\n icon: CROSSxIcon,\n featured: true,\n },\n cross_extension: {\n id: \"cross_extension\",\n name: \"CROSSx 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","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, #e7f9f8);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, #00a0a2);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:var(--ds-accent-primary-default, #00b4b7);border-radius:6px;font-family:Inter,sans-serif;font-weight:500;font-size:12px;line-height:1;letter-spacing:0;color:var(--ds-accent-primary-on, #ffffff);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-accent-primary-high, #00a0a2);text-decoration:none}.wcm-terms-link:hover{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 * 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 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: \"CROSSx\",\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-primary-default, #00b4b7);--cb-bg-hover: var(--cb-bg);--cb-color: var(--ds-accent-primary-on-primary, #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=\"none\" width=\"100%\" height=\"100%\" overflow=\"visible\" style=\"display: block;\" viewBox=\"0 0 36 36\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#c0)\"><path d=\"M18 36C27.9411 36 36 27.9411 36 18C36 8.05887 27.9411 0 18 0C8.05887 0 0 8.05887 0 18C0 27.9411 8.05887 36 18 36Z\" fill=\"#00D5AA\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M18.2903 2.57169L21.3525 5.63385V10.6471L25.3058 6.69381L29.6364 11.0244L25.7226 14.9381H18.2903C16.5992 14.9381 15.2283 16.309 15.2283 18.0003C15.2283 19.6915 16.5992 21.0624 18.2903 21.0624H25.6836L29.6365 25.0154L25.3059 29.3459L21.3525 25.3925V30.3666L18.2903 33.4288L15.2283 30.3666V25.4326L11.3148 29.3459L6.98426 25.0154L10.9372 21.0624H5.92394L2.86178 18.0003L5.92394 14.9381H10.8981L6.98437 11.0244L11.3149 6.69383L15.2283 10.6071V5.63385L18.2903 2.57169Z\" fill=\"#000\"/></g><defs><clipPath id=\"c0\"><rect width=\"36\" height=\"36\" fill=\"#fff\"/></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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C,EACA,MAAAyD,GACA,eAAAxD,EACA,cAAAC,EACA,MAAAC,EACA,qBAAAC,EACA,wBAAAC,EACA,sBAAAC,EACA,aAAcC,EAAe8D,GAAmB,OAChD,gBAAA7D,EACA,SAAAC,EACA,WAAAE,EACA,WAAAC,EACA,aAAAE,GACA,YAAawB,GACb,WAAAoB,GACA,YAAAE,GACA,cAAAD,GACA,eAAAE,GACA,cAAAC,GACA,iBAAAC,GACA,KAAAC,GACA,QAAAC,EACA,kBAAAC,GACA,qBAAAC,GACA,cAAAlD,GACA,eAAAC,EACA,kBAAAC,EACA,gBAAAC,GACA,iBAAAC,GACA,gBAAAC,EACA,sBAAAC,GACA,YAAAC,GACA,UAAAC,EACA,gBAAAC,GACA,qBAAAC,GACA,aAAAC,EACA,cAAAC,GACA,eAAAC,GACA,kBAAAC,GACA,kBAAAC,GACA,cAAAC,GACA,aAAAC,GACA,UAAAC,GACA,SAAAC,GACA,OAAAC,EACF,GACA,CACEG,GACAzD,EACA+D,GACAQ,GACAN,GACAE,GACA/D,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAgD,GACAC,GACA7C,EACAyD,GACAxD,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACA8D,GACA7D,EACAC,EACAE,EACAC,EACAE,GACAwB,GACAoB,GACAE,GACAE,GACAE,GACAE,GACAjD,GACAC,EACAC,EACAC,GACAC,GACAC,EACAC,GACAC,GACAC,EACAC,GACAC,GACAC,EACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,GACAC,EACF,CACF,EAEA,SACE,QAACmC,GAAkB,SAAlB,CAA2B,MAAOD,GAChC,SAAAzB,MACC,QAAC,UAAO,KAAP,CAEC,UAAWE,GACX,KAAMM,GACN,aAAcC,GACd,WAAYN,GACZ,aAAY,GACZ,iBAAkB,GAEjB,SAAAV,IARG,QASN,KAEA,QAAS,QAAR,CAA2B,KAAMe,GAAM,aAAcC,GACnD,SAAAhB,IADe,SAElB,EAEJ,CAEJ,CAEO,IAAMkC,GAAa,OAAO,OAAO5F,GAAgB,CACtD,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","names":["index_exports","__export","APPLE_ICON","AppLauncher","AppLauncherContent","AppLauncherTrigger","BINANCE_ICON","BridgeFlow","CONNECTOR_REGISTRY","CROSSX_ICON","ConnectButton","ConnectorId","DEFAULT_SKILLS_HREF","DappUiErrorBoundary","GOOGLE_ICON","METAMASK_ICON","PORTFOLIO_SECTIONS","SOCIAL_REGISTRY","SendFlow","SkillsButton","TOKEN_STATS_QUERY_KEY","USER_BALANCE_QUERY_KEY","WALLET_REGISTRY","WalletConnectModal","WalletInfo","WalletPortfolio","WalletPortfolioBody","announceAppLauncherUsage","captureDappUiException","getDappUiSentryScope","initDappUiSentry","normalizeFailureReason","resolveEnvironment","setDappUiAnalyticsUser","trackDappUiEvent","trackDappUiFunnel","useGlobalMenu","useTokenBalance","useTokenStats","useWalletDetect","__toCommonJS","import_meta","readViteEnv","key","readNodeEnv","resolveEnvironment","env","normalizeEnvironment","crossxEnv","value","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","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","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","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","signal","res","GLOBAL_MENU_URLS","error","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","handlePointerDown","e","currentPathname","handleItemClick","dx","dy","url","isExternal","isExternalLink","urlPathname","renderMenuItem","serviceStatus","getGlobalMenuItemStatus","isEndingSoon","iconUrl","resolveGlobalMenuItemAssetUrl","isActive","menuContent","TrackingBoundary","cn","BackArrowIcon","CloseIcon","linkHostname","React","Popover","import_vaul","import_react","WalletInfoContext","useWalletInfoContext","ctx","ConnectorId","CONNECTOR_REGISTRY","React","import_jsx_runtime","WalletIcon","size","className","CopyIcon","CheckIcon","CloseIcon","DisconnectIcon","QRIcon","BackIcon","ChevronDownIcon","size","className","EyeOpenIcon","EyeClosedIcon","QRLogoIcon","size","className","SendActionIcon","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","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","useRemoteConfigValue","file","select","setValue","selectRef","alive","next","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","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","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_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_OVERVIEW_URL","useCrossdApr","data","rest","signal","res","json","ratio","import_react_query","import_react","TOKEN_INFO_QUERY_KEY","buildCategoryKey","chainId","address","useTokenInfo","env","enabled","data","rest","signal","baseUrl","getApiBase","res","json","map","info","key","import_react","import_bignumber","useTotalValue","env","walletAddress","enabled","tokens","isBalanceLoading","useTokenBalance","statsMap","useTokenStats","total","BigNumber","t","stat","buildStatsKey","value","totalUsd","displayValue","isLowerBound","formatTokenValueForDisplay","import_qrcode","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","formatWalletAddress","address","shortenTransactionHash","hash","WITHDRAW_ERC20_SELECTOR","encodeWithdrawErc20","poolId","amount","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","ClipboardIcon","size","className","CheckIcon","SuccessCheckIcon","ExternalLinkIcon","size","className","React","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","TOKEN_INFO_QUERY_KEY","useTokenInfo","env","enabled","crossChainId","CHAIN_IDS","data","rest","signal","baseUrl","getApiBase","res","json","byChainAddr","byChainSym","addrMap","symMap","t","addr","sym","chainSymKey","address","symbol","byChain","byAddr","byChainSymbol","bySym","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","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","SectionCard","sectionKey","title","icon","iconFit","isEmpty","loading","children","reporter","useSectionStatusReporter","React","import_jsx_runtime","INPUT_RE","QUICK_ADD_AMOUNTS","GAS_ESTIMATE_DEBOUNCE_MS","formatFeeValue","estimate","status","feePerGas","formatNativeFeeFromWei","formatGasMetric","value","unit","TokenBadge","position","size","WithdrawDialog","open","positions","initialPoolAddress","walletAddress","txHash","error","copied","estimateWithdraw","onClose","onConfirm","onCopyHash","trackFunnel","selectedPoolAddress","setSelectedPoolAddress","amountInput","setAmountInput","gasEstimate","setGasEstimate","gasStatus","setGasStatus","gasRequestRef","debounceRef","lastEstimateKeyRef","selected","key","p","depositedRaw","decimals","amountRaw","parsed","parseTokenAmount","exceedsBalance","canSubmit","resetAmountAndGas","handleSelect","estimateKey","requestId","handleInputChange","event","next","setAmountFromRaw","raw","formatRawTokenAmount","handleQuickAdd","add","handleMax","isSubmitting","priorityFee","formatGweiFromWei","maxFee","gasLimit","formatGasLimit","depositedDisplay","formatPortfolioTokenAmount","SuccessCheckIcon","shortenTransactionHash","CheckIcon","ClipboardIcon","isSelected","formatWalletAddress","React","import_react_query","import_react_query","REWARD_FACTORY_INFO_QUERY_KEY","useRewardFactoryInfo","env","enabled","data","rest","signal","baseUrl","getApiBase","res","json","WP_QUERY_PREFIX","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","query","openWithdraw","poolAddress","closeWithdraw","estimateWithdraw","position","tx","from","copyHash","confirmWithdraw","estimate","hash","err","normalizeFailureReason","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","flow","useWithdrawFlow","positions","useWithdrawPositions","dialogPositions","p","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","canWithdraw","inlineWithdrawReady","e","handleOutlinkClick","ExternalLinkIcon","WithdrawDialog","React","import_jsx_runtime","SECTION_TITLE","PointsSection","env","walletAddress","onOutlink","useWalletPortfolioContext","fiat","usePortfolioFiat","hasAddress","getTokenImage","useTokenInfo","flow","useWithdrawFlow","positions","positionsLoading","useWithdrawPositions","pointPositions","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","SECTION_TITLE","formatApr","apr","value","BigNumber","StakeSection","env","walletAddress","onOutlink","stakingRewardsReader","useWalletPortfolioContext","fiat","usePortfolioFiat","hasAddress","stakeInfo","stakeLoading","useStakeInfo","latestReward","aprAccrued","rewardLoading","useStakeLatestReward","onchain","useStakingOnchain","crossUsdPrice","useCrossUsdPrice","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","tokenASymbol","tokenBSymbol","lpDecimals","sharePercent","valueText","balance","totalSupply","ratio","positionCross","crossUsd","formatFiatFromUsd","GameSwapSection","env","walletAddress","onOutlink","lpBalanceReader","useWalletPortfolioContext","usePortfolioFiat","hasAddress","pools","poolsLoading","useGameSwapPools","pairAddresses","p","balances","balancesLoading","useGameSwapLpBalances","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","PORTFOLIO_SECTIONS","styleInject","import_jsx_runtime","POLL_SEC","WP_QUERY_PREFIX","WalletPortfolioBody","env","theme","walletAddress","walletName","onBack","showHeader","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","query","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","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","import_jsx_runtime","SearchIcon","size","className","HistoryIcon","SlidersIcon","ArrowDownIcon","ArrowRightIcon","ArrowRightLeftIcon","React","import_bignumber","isPositivePrice","value","formatFallbackAmount","BigNumber","canUseOneToOneFallback","input","getFallbackQuote","fromPrice","toPrice","sourceAmount","amount","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","n","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","DEFAULT_TERMS_URL","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","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","BigNumber","balance","formatBalanceUsd","formatTokenPriceUsd","formatRateDecimal","trimmed","integerWithCommas","shortHash","hash","formatHistoryTimestamp","timestamp","date","pad","formatHistoryTokenAmount","formatHistoryUsd","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","BridgeAmountPanel","label","usd","onAmountChange","onTokenClick","locked","event","ChevronDownIcon","BridgeTokenPicker","side","onClose","titleId","query","setQuery","portalTarget","setPortalTarget","selectedToken","handleKeyDown","selectableTokens","filteredTokens","q","handleSelect","CloseIcon","SearchIcon","isSelected","BridgeSlippageSettings","selectedPreset","preset","customDraft","setCustomDraft","hasCustomDraft","customValue","exceedsMax","highSlippage","selectPreset","applyCustom","BridgeTermsModal","checked","setChecked","CopyButton","disabled","copied","setCopied","BridgeApproveModal","approval","isApproving","BridgeApprovalSuccessModal","isContinuing","ExchangeRate","fromToken","toToken","isRateReversed","setIsRateReversed","exchangeRate","fromSymbol","toSymbol","ArrowRightLeftIcon","BridgeStatusToast","BridgeErrorToast","visible","setVisible","timer","successTitle","BridgeSuccessModal","submitted","index","InfoTokenGlyph","hasImage","InfoRowValue","row","InfoRows","rows","getInfoTitle","summary","hasBridgeInfo","hasSwapInfo","OrderedInfoSections","showNestedTitles","sections","section","BridgeDetails","hasTxFee","infoTitle","infoOpen","setInfoOpen","txFeeOpen","setTxFeeOpen","open","HistoryCard","fiatAmount","ArrowRightIcon","HistoryView","setFilter","hasPending","visibleHistory","emptyMessage","FormView","env","tokenPickerSide","setTokenPickerSide","slippageSettingsOpen","setSlippageSettingsOpen","swapRotation","setSwapRotation","poweredByUrl","useRemoteConfigValue","config","resolveEnvValue","fieldErrors","insufficientBalance","fromError","commonQuoteError","toastError","hasPendingHistory","showSlippage","HistoryIcon","SlidersIcon","r","ArrowDownIcon","BridgeFlow","onBackToWallet","className","rest","resolvedEnv","resolveEnvironment","useBridgeFlow","rootRef","title","TrackingBoundary","cn","BackIcon","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","WalletInfoContent","align","sideOffset","className","children","theme","isMobile","open","drawerDirection","modal","showBalance","showForgeToken","showGameToken","showQR","showBridge","qrLogoSrc","walletAddress","accountName","sendAccounts","profileImageUrl","preferredTokens","close","onSelectWallet","onCopyAddress","onBuy","onDisconnect","disconnectLabel","termsUrl","termsLabel","privacyUrl","privacyLabel","portalStyle","navContent","navPosition","footerContent","view","setViewFromCtx","selectedSendToken","setSelectedSendToken","showPortfolio","portfolioTitle","portfolioSections","showTotalAssets","totalAssetsLabel","sendTransaction","getTransactionReceipt","estimateGas","onOutlink","lpBalanceReader","stakingRewardsReader","bridgeTokens","bridgeHistory","getBridgeQuote","getBridgeToTokens","getBridgeApproval","approveBridge","submitBridge","onReceive","onSend","onBuyDisabledMessage","onBridgeDisabledMessage","onSendDisabledMessage","useWalletInfoContext","actionToast","setActionToast","actionToastTimerRef","showActionToast","message","buyDisabledByMessage","handleBuyClick","result","bridgeDisabledByMessage","sendDisabledByMessage","rawTokens","isLoading","useTokenBalance","statsMap","useTokenStats","crossdAprPercent","useCrossdApr","categoryMap","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","BigNumber","buildCategoryKey","a","b","aIdx","bIdx","statA","buildStatsKey","statB","valueA","sortedForgeHoldings","balanceAddressSet","hasTransitioned","sendToast","setSendToast","sendToastTimerRef","toastChainId","sendToastUrl","useTransactionExplorerUrl","setView","sendInitialStep","setSendInitialStep","handleBackToWallet","handleSelectSendToken","initialStep","remoteBridgeWebUrl","useRemoteConfigValue","config","resolveEnvValue","remoteGametokenSites","handleBridgeClick","handleSendSubmitted","info","shortenAddress","handleConfirmSendSuccess","txHash","handleSendFailed","effectiveReceive","effectiveSend","first","handleSendClick","scrollRef","el","stop","e","copied","setCopied","copyTimerRef","accountModalOpen","setAccountModalOpen","handleClose","handleKey","timer","footerEl","updateFooterHeight","observer","handleCopyAddress","handleImageLoad","avatarStyle","resolvedProfileUrl","getPortraitUrl","totalSplit","legalLinks","handleOutlinkClick","defaultDisconnectButton","DisconnectIcon","defaultQuickActions","cn","BuyActionIcon","BridgeActionIcon","QRIcon","SendActionIcon","PortfolioActionIcon","walletView","prev","EyeClosedIcon","EyeOpenIcon","_","stat","change","isPositive","chainBadgeUrl","getChainBadgeUrl","shouldShowApr","isCrossdTokenAddress","formatPrice","formatWei","formatTokenValue","holding","imgUrl","portfolioView","WalletPortfolioBody","sendView","SendFlow","n","resolvedBridgeTokens","price","formatRawTokenAmount","bridgeView","BridgeFlow","isSwitchableAccount","receiveAccountContent","ChevronDownIcon","qrView","QRLogoIcon","CheckIcon","CopyIcon","isSubView","effectiveFooter","accountTextBlock","contentBody","BackIcon","CloseIcon","ToastCheckIcon","contentStyle","TrackingBoundary","React","WalletInfoNav","position","children","setNavContent","setNavPosition","useWalletInfoContext","React","WalletInfoFooter","children","setFooterContent","useWalletInfoContext","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","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","navContent","setNavContent","navPosition","setNavPosition","footerContent","setFooterContent","view","setView","selectedSendToken","setSelectedSendToken","setDappUiAnalyticsUser","handleDisconnect","clearRecentSendAddresses","contextValue","WalletInfoContext","WalletInfo","WalletInfoTrigger","WalletInfoContent","WalletInfoNav","WalletInfoFooter","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","Dialog","import_vaul","import_jsx_runtime","CROSSxIcon","MetaMaskIcon","BinanceIcon","Verse8Icon","TronIcon","GoogleIcon","AppleIcon","CloseIcon","WALLET_REGISTRY","CROSSxIcon","MetaMaskIcon","BinanceIcon","Verse8Icon","TronIcon","resolveWallet","id","SOCIAL_REGISTRY","GoogleIcon","AppleIcon","resolveSocial","import_jsx_runtime","runConnectHandler","id","handler","trackDappUiFunnel","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","isDetected","useWalletDetect","activeWalletIds","activeSocialIds","mergedStyle","sizeVars","contextValue","DEFAULT_TERMS_URL","DEFAULT_PRIVACY_URL","wrapper","WalletConnectModalContext","WalletConnectModal","WalletConnectModalTrigger","WalletConnectModalContent","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","_","success","shorten","ConnectWalletIcon","GOOGLE_ICON","APPLE_ICON","CROSSX_ICON","METAMASK_ICON","BINANCE_ICON","PROVIDER_NAME","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"]}
|