{"_id":"@abcwalletio/evm","name":"@abcwalletio/evm","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@abcwalletio/evm","version":"1.0.0","description":"ABC WaaS EVM adapter — MPC-based viem Custom Account, Wagmi v2 connector, and EIP-1193 provider","keywords":["waas","evm","ethereum","wagmi","viem","eip-1193","mpc","wallet-connector"],"license":"MIT","author":{"name":"Ahnlab Blockchain Company"},"homepage":"http://abcwaas.com/","publishConfig":{"access":"public"},"engines":{"node":">=18.0.0"},"sideEffects":false,"type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./wagmi":{"types":"./dist/wagmi.d.ts","import":"./dist/wagmi.js","require":"./dist/wagmi.cjs"}},"typesVersions":{"*":{"wagmi":["./dist/wagmi.d.ts"]}},"peerDependencies":{"viem":">=2.0.0","@abcwalletio/core":"^1.0.0"},"peerDependenciesMeta":{"wagmi":{"optional":true},"@wagmi/core":{"optional":true},"react":{"optional":true},"react-dom":{"optional":true}},"devDependencies":{"@types/react":"^18.0.0","@types/react-dom":"^18.0.0","@wagmi/core":"^2.16.0","react":"^18.0.0","react-dom":"^18.0.0","wagmi":"^2.14.0","tsup":"^8.0.0","typescript":"^5.4.0","viem":"^2.0.0","vitest":"^1.4.0","@abcwalletio/core":"^1.0.0"},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run"},"_id":"@abcwalletio/evm@1.0.0","_integrity":"sha512-LEi6kwkGYBY+5jLmT4TK0auohA03CYceTH9pYXvkcVpKTV7pJW2SWY0eqVrMV9dGDQ7xHLVBqVpFwL9IJbWHYA==","_resolved":"/private/var/folders/lt/4x5qd11x2wl8sh4gvr7y0pp80000gn/T/79795b989351383a66a60de3ec61847c/abcwalletio-evm-1.0.0.tgz","_from":"file:abcwalletio-evm-1.0.0.tgz","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-LEi6kwkGYBY+5jLmT4TK0auohA03CYceTH9pYXvkcVpKTV7pJW2SWY0eqVrMV9dGDQ7xHLVBqVpFwL9IJbWHYA==","shasum":"3de4ae0f64b5f5b71c2d79540f27d8c560e944b4","tarball":"https://registry.npmjs.org/@abcwalletio/evm/-/evm-1.0.0.tgz","fileCount":19,"unpackedSize":699882,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDerBuIpDAXMYbahTIleIIRQ1CHvvhSLlLvd/cCDlCrLwIgeBoqTzunmUp11M3Qbjvy918zzwA9XGaH95umbdkPMRM="}]},"_npmUser":{"name":"theo_ahnlabio","email":"theo@ahnlab.com"},"directories":{},"maintainers":[{"name":"abc-admin","email":"abc_it@ahnlab.com"},{"name":"1kko-abc","email":"1kko@ahnlab.com"},{"name":"theo_ahnlabio","email":"theo@ahnlab.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/evm_1.0.0_1776818587753_0.08146344397283589"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-22T00:43:07.676Z","1.0.0":"2026-04-22T00:43:07.932Z","modified":"2026-04-22T00:43:08.136Z"},"maintainers":[{"name":"abc-admin","email":"abc_it@ahnlab.com"},{"name":"1kko-abc","email":"1kko@ahnlab.com"},{"name":"theo_ahnlabio","email":"theo@ahnlab.com"}],"description":"ABC WaaS EVM adapter — MPC-based viem Custom Account, Wagmi v2 connector, and EIP-1193 provider","homepage":"http://abcwaas.com/","keywords":["waas","evm","ethereum","wagmi","viem","eip-1193","mpc","wallet-connector"],"author":{"name":"Ahnlab Blockchain Company"},"license":"MIT","readme":"# @abcwalletio/evm\n\nABC WAAS SDK의 EVM 통합 패키지입니다. 두 가지 진입점을 제공합니다:\n\n- **`@abcwalletio/evm`** — `toAbcWaasAccount()`로 viem Custom Account 생성 (Wagmi 없이도 사용 가능)\n- **`@abcwalletio/evm/wagmi`** — Wagmi v2 호환 커넥터, EIP-1193 Provider, 모달 UI\n\n## Installation\n\n```bash\npnpm add @abcwalletio/evm @abcwalletio/core\n```\n\n## Peer Dependencies\n\n- `@abcwalletio/core` (workspace peer)\n- `viem@^2.0.0`\n- (Wagmi 경로 사용 시) `wagmi@^2.0.0`, `@wagmi/core@^2.0.0`\n\n## viem Custom Account 사용\n\nWagmi 없이 viem `WalletClient`를 직접 만들 때는 `toAbcWaasAccount`를 사용합니다.\nEIP-155 replay protection을 위해 legacy tx는 `{r, s, v: BigInt(27+yParity)}`로,\nEIP-1559/2930은 `{r, s, yParity}`로 viem의 `serializeTransaction`에 전달합니다.\n\n```typescript\nimport { createWaasSDK, MemoryStorage } from '@abcwalletio/core';\nimport { createWalletClient, http, parseEther } from 'viem';\nimport { sepolia } from 'viem/chains';\nimport { toAbcWaasAccount } from '@abcwalletio/evm';\n\nconst sdk = await createWaasSDK({ /* ... */ storage: new MemoryStorage() });\n\nconst account = await toAbcWaasAccount({\n  sdk,\n  requestPassword: async () => prompt('PIN?') ?? '',\n});\n\nconst client = createWalletClient({ account, chain: sepolia, transport: http() });\nconst hash = await client.sendTransaction({\n  to: '0xabc...',\n  value: parseEther('0.01'),\n});\n```\n\n## Quick Start\n\n### Basic Setup (V2 — Secure Channel)\n\n```typescript\nimport { createConfig, http } from '@wagmi/core';\nimport { mainnet, sepolia } from 'viem/chains';\nimport { abcWaasConnector } from '@abcwalletio/evm/wagmi';\nimport { MemoryStorage } from '@abcwalletio/core';\n\nconst config = createConfig({\n  chains: [mainnet, sepolia],\n  transports: {\n    [mainnet.id]: http(),\n    [sepolia.id]: http(),\n  },\n  connectors: [\n    abcWaasConnector({\n      sdkConfig: {\n        storage: new MemoryStorage(),\n        auth: {\n          v2: {\n            clientId: 'your-client-id',\n            clientSecret: 'your-client-secret',\n          },\n        },\n      },\n    }),\n  ],\n});\n```\n\n### Basic Setup (V3 — TokenProvider)\n\n고객사 백엔드가 WaaS 토큰을 발급하는 엔터프라이즈 환경에선 사전 초기화한 SDK를\n커넥터에 주입합니다. 브라우저에 `clientSecret`이 필요 없고, V3가 이미 인증된\n상태이므로 로그인 모달은 자동 skip되며 MPC 키 설정 모달만 뜹니다.\n\n```typescript\nimport { createWaasSDK, LocalStorageAdapter } from '@abcwalletio/core';\nimport { abcWaasConnector } from '@abcwalletio/evm/wagmi';\n\nconst sdk = await createWaasSDK({\n  storage: new LocalStorageAdapter(),\n  auth: {\n    v3: {\n      tokenProvider: async () => {\n        const r = await fetch('/api/waas/token', { credentials: 'include' });\n        if (!r.ok) throw new Error('unauthorized');\n        const { access_token, expires_in } = await r.json();\n        return { accessToken: access_token, expiresIn: expires_in };\n      },\n    },\n  },\n});\n\nconst config = createConfig({\n  chains: [mainnet, sepolia],\n  transports: {\n    [mainnet.id]: http(),\n    [sepolia.id]: http(),\n  },\n  connectors: [abcWaasConnector({ sdk })],\n});\n```\n\n### With React\n\n```tsx\nimport { WagmiProvider, useConnect, useAccount, useSignMessage } from 'wagmi';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { abcWaasConnector } from '@abcwalletio/evm/wagmi';\nimport { createWaasSDK, LocalStorageAdapter } from '@abcwalletio/core';\n\n// SDK 사전 초기화 (선택적)\nconst sdk = await createWaasSDK({\n  storage: new LocalStorageAdapter(),\n  auth: {\n    v2: {\n      clientId: 'your-client-id',\n      clientSecret: 'your-client-secret',\n    },\n  },\n});\n\n// Wagmi Config\nconst config = createConfig({\n  chains: [mainnet, sepolia],\n  transports: {\n    [mainnet.id]: http(),\n    [sepolia.id]: http(),\n  },\n  connectors: [\n    abcWaasConnector({ sdk }), // 사전 초기화된 SDK 사용\n  ],\n});\n\nconst queryClient = new QueryClient();\n\nfunction App() {\n  return (\n    <WagmiProvider config={config}>\n      <QueryClientProvider client={queryClient}>\n        <WalletComponent />\n      </QueryClientProvider>\n    </WagmiProvider>\n  );\n}\n\nfunction WalletComponent() {\n  const { connect, connectors } = useConnect();\n  const { address, isConnected } = useAccount();\n  const { signMessage } = useSignMessage();\n\n  const abcConnector = connectors.find(c => c.id === 'abcWaas');\n\n  if (isConnected) {\n    return (\n      <div>\n        <p>Connected: {address}</p>\n        <button onClick={() => signMessage({ message: 'Hello!' })}>\n          Sign Message\n        </button>\n      </div>\n    );\n  }\n\n  return (\n    <button onClick={() => connect({ connector: abcConnector! })}>\n      Connect ABC Wallet\n    </button>\n  );\n}\n```\n\n## Configuration Options\n\n```typescript\ninterface AbcWaasConnectorConfig {\n  /** SDK 설정 (SDK 인스턴스가 제공되지 않은 경우 사용) */\n  sdkConfig?: WaasSDKConfig;\n\n  /** 기존 SDK 인스턴스 (있으면 sdkConfig 무시) */\n  sdk?: WaasSDK;\n\n  /** 커넥터 표시 이름 (기본값: 'ABC Waas') */\n  name?: string;\n\n  /** 커넥터 아이콘 URL */\n  icon?: string;\n\n  /** Modal 표시 여부 (기본값: true) */\n  showLoginModal?: boolean;\n\n  /** headless 모드 콜백 (showLoginModal: false일 때) */\n  onLoginRequired?: (resolve: () => void) => void;\n  onMpcKeyRequired?: (resolve: () => void) => void;\n\n  /** Modal 설정 (showLoginModal: true일 때) */\n  modalConfig?: {\n    theme?: 'light' | 'dark';\n    language?: 'ko' | 'en';\n    loginMethods?: ('email' | 'google' | 'apple' | 'naver' | 'kakao' | 'line')[];\n  };\n}\n```\n\n### Using SDK Instance\n\n사전 초기화된 SDK 인스턴스를 전달하면 연결 시 초기화 시간을 절약할 수 있습니다.\n\n```typescript\nimport { createWaasSDK, LocalStorageAdapter } from '@abcwalletio/core';\n\n// SDK 먼저 초기화\nconst sdk = await createWaasSDK({\n  storage: new LocalStorageAdapter(),\n  auth: {\n    v2: {\n      clientId: 'your-client-id',\n      clientSecret: 'your-client-secret',\n    },\n  },\n});\n\n// 인증 수행 (V2 — Secure Channel 이메일/비번)\nawait sdk.auth.v2.loginWithEmail('user@example.com', 'password');\n\n// 또는 V3 — createWaasSDK({ auth: { tokenProvider } })로 초기화한 경우\n// 최초 API 호출 시 자동으로 tokenProvider가 불린다.\n\n// MPC 키 생성\nawait sdk.mpc.generateKeyShare('secp256k1', 'password123');\n\n// 커넥터에 SDK 전달\nconst connector = abcWaasConnector({ sdk });\n```\n\n### Using SDK Config\n\n`sdkConfig`를 전달하면 connect 시점에 SDK가 자동 초기화됩니다. `auth`에는 V2/V3\n중 원하는 경로를 넣으면 됩니다 (둘 다 넣어 공존도 가능).\n\n```typescript\n// V2 — Secure Channel 이메일/비번\nconst connector = abcWaasConnector({\n  sdkConfig: {\n    storage: new MemoryStorage(),\n    auth: {\n      v2: {\n        clientId: 'your-client-id',\n        clientSecret: 'your-client-secret',\n      },\n    },\n  },\n});\n\n// V3 — 고객사 백엔드가 토큰 발급\nconst connector = abcWaasConnector({\n  sdkConfig: {\n    storage: new LocalStorageAdapter(),\n    auth: {\n      v3: {\n        tokenProvider: async () => {\n          const r = await fetch('/api/waas/token', { credentials: 'include' });\n          const { access_token, expires_in } = await r.json();\n          return { accessToken: access_token, expiresIn: expires_in };\n        },\n      },\n    },\n  },\n  // V3는 이미 인증된 상태로 진입하므로 로그인 모달은 자동 skip됨 (MPC 키 모달만 뜸)\n});\n```\n\n## Supported RPC Methods\n\n### Account Methods\n\n| Method | Description |\n|--------|-------------|\n| `eth_requestAccounts` | 계정 연결 요청 |\n| `eth_accounts` | 연결된 계정 목록 |\n\n### Chain Methods\n\n| Method | Description |\n|--------|-------------|\n| `eth_chainId` | 현재 체인 ID |\n| `wallet_switchEthereumChain` | 체인 전환 |\n\n### Signing Methods\n\n| Method | Description |\n|--------|-------------|\n| `personal_sign` | EIP-191 개인 메시지 서명 |\n| `eth_sign` | 원시 해시 서명 (deprecated) |\n| `eth_signTypedData_v4` | EIP-712 타입화된 데이터 서명 |\n| `eth_signTransaction` | 트랜잭션 서명 |\n| `eth_sendTransaction` | 트랜잭션 서명 및 전송 |\n\n#### Transaction Parameter Auto-fill\n\n`eth_signTransaction` 및 `eth_sendTransaction` 호출 시, 누락된 트랜잭션 파라미터가 자동으로 채워집니다:\n\n| Parameter | Auto-fill Method |\n|-----------|-----------------|\n| `nonce` | `eth_getTransactionCount` (pending) |\n| `gas` | `eth_estimateGas` |\n| `gasPrice` (Legacy) | `eth_gasPrice` |\n| `maxFeePerGas` (EIP-1559) | `baseFeePerGas * 1.2 + maxPriorityFeePerGas` |\n| `maxPriorityFeePerGas` (EIP-1559) | `eth_maxPriorityFeePerGas` 또는 1 gwei |\n\n네트워크의 EIP-1559 지원 여부는 최신 블록의 `baseFeePerGas` 존재 여부로 자동 감지됩니다. 트랜잭션 요청에 이미 값이 포함된 경우 해당 값이 우선 사용됩니다.\n\n### Read Methods (Proxy to RPC)\n\n| Method | Description |\n|--------|-------------|\n| `eth_blockNumber` | 최신 블록 번호 |\n| `eth_getBalance` | 잔액 조회 |\n| `eth_getTransactionCount` | 논스 조회 |\n| `eth_call` | 컨트랙트 호출 (읽기) |\n| `eth_estimateGas` | 가스 추정 |\n| `eth_gasPrice` | 현재 가스 가격 |\n\n## Provider Events\n\n```typescript\nconst provider = await connector.getProvider();\n\n// 계정 변경\nprovider.on('accountsChanged', (accounts: string[]) => {\n  console.log('Accounts changed:', accounts);\n});\n\n// 체인 변경\nprovider.on('chainChanged', (chainId: string) => {\n  console.log('Chain changed:', chainId);\n});\n\n// 연결\nprovider.on('connect', (info: { chainId: string }) => {\n  console.log('Connected:', info.chainId);\n});\n\n// 연결 해제\nprovider.on('disconnect', (error: ProviderRpcError) => {\n  console.log('Disconnected:', error.message);\n});\n```\n\n## Direct Provider Usage\n\n커넥터 없이 Provider를 직접 사용할 수도 있습니다.\n\n```typescript\nimport { AbcWaasProvider } from '@abcwalletio/evm/wagmi';\nimport { createWaasSDK, MemoryStorage } from '@abcwalletio/core';\nimport { mainnet, sepolia } from 'viem/chains';\n\n// SDK 초기화\nconst sdk = await createWaasSDK({\n  storage: new MemoryStorage(),\n  auth: {\n    v2: {\n      clientId: 'your-client-id',\n      clientSecret: 'your-client-secret',\n    },\n  },\n});\n\n// 키 생성 (필수)\nawait sdk.mpc.generateKeyShare('secp256k1', 'password123');\n\n// Provider 생성\nconst provider = new AbcWaasProvider({\n  sdk,\n  chainId: mainnet.id,\n  chains: [mainnet, sepolia],\n});\n\n// 계정 연결\nconst accounts = await provider.request({ method: 'eth_requestAccounts' });\nconsole.log('Accounts:', accounts);\n\n// 메시지 서명\nconst signature = await provider.request({\n  method: 'personal_sign',\n  params: ['Hello, World!', accounts[0]],\n});\nconsole.log('Signature:', signature);\n```\n\n## Error Handling\n\n```typescript\nimport { RpcErrorCodes } from '@abcwalletio/evm/wagmi';\n\ntry {\n  await provider.request({ method: 'eth_requestAccounts' });\n} catch (error) {\n  if (error.code === RpcErrorCodes.UNAUTHORIZED) {\n    // MPC 키가 없음 - 키 생성 필요\n  } else if (error.code === RpcErrorCodes.USER_REJECTED) {\n    // 사용자가 요청 거부\n  } else if (error.code === RpcErrorCodes.CHAIN_NOT_ADDED) {\n    // 체인이 설정에 없음\n  }\n}\n```\n\n### RPC Error Codes\n\n| Code | Name | Description |\n|------|------|-------------|\n| 4001 | USER_REJECTED | 사용자가 요청 거부 |\n| 4100 | UNAUTHORIZED | 권한 없음 (키 없음) |\n| 4200 | UNSUPPORTED_METHOD | 지원하지 않는 메서드 |\n| 4900 | DISCONNECTED | 연결 해제됨 |\n| 4901 | CHAIN_DISCONNECTED | 체인 연결 해제 |\n| 4902 | CHAIN_NOT_ADDED | 체인 미등록 |\n| -32601 | METHOD_NOT_FOUND | 메서드 없음 |\n| -32602 | INVALID_PARAMS | 잘못된 파라미터 |\n| -32603 | INTERNAL_ERROR | 내부 에러 |\n\n## SDK Event Integration\n\n커넥터는 SDK의 인증 이벤트를 자동으로 처리합니다. `authError` 또는 `logout` 이벤트 발생 시 자동으로 연결이 해제됩니다.\n\n### 자동 연결 해제\n\n```typescript\n// 커넥터가 connect() 시점에 SDK 이벤트를 자동 구독합니다\n// - authError 발생 시: 자동으로 disconnect 이벤트 발생\n// - logout 발생 시: 자동으로 disconnect 이벤트 발생\n\n// Wagmi에서 disconnect 이벤트를 감지하여 UI 업데이트 가능\nconst { isConnected } = useAccount();\n\nuseEffect(() => {\n  if (!isConnected) {\n    // 연결이 해제됨 (토큰 만료, 로그아웃 등)\n    // 로그인 페이지로 이동하거나 재연결 UI 표시\n  }\n}, [isConnected]);\n```\n\n### SDK 이벤트 직접 구독\n\n커넥터와 별도로 SDK 이벤트를 직접 구독할 수도 있습니다.\n\n```typescript\nimport { createWaasSDK, type WaasEventHandler } from '@abcwalletio/core';\n\nconst sdk = await createWaasSDK({ ... });\n\n// 토큰 자동 갱신 알림\nsdk.on('tokenRefreshed', (payload) => {\n  console.log('토큰이 갱신되었습니다');\n});\n\n// 인증 에러 처리\nsdk.on('authError', (payload) => {\n  console.error('인증 에러:', payload.error.message);\n  // 사용자에게 알림 표시\n  alert('세션이 만료되었습니다. 다시 로그인해주세요.');\n});\n\n// 로그아웃 처리\nsdk.on('logout', () => {\n  console.log('로그아웃되었습니다');\n  // 로그인 페이지로 이동\n  window.location.href = '/login';\n});\n```\n\n### React Hook 예제\n\n```tsx\nimport { useEffect } from 'react';\nimport { useAccount, useDisconnect } from 'wagmi';\nimport { getSDK } from './lib/sdk'; // SDK 싱글톤\n\nfunction useAuthEvents() {\n  const { isConnected } = useAccount();\n  const { disconnect } = useDisconnect();\n\n  useEffect(() => {\n    const sdk = getSDK();\n    if (!sdk) return;\n\n    const handleAuthError = () => {\n      // 커넥터가 자동으로 disconnect하지만,\n      // 추가적인 UI 처리가 필요한 경우 여기서 수행\n      console.log('인증 에러 발생 - 로그인 페이지로 이동');\n    };\n\n    const handleLogout = () => {\n      console.log('로그아웃 완료');\n    };\n\n    sdk.on('authError', handleAuthError);\n    sdk.on('logout', handleLogout);\n\n    return () => {\n      sdk.off('authError', handleAuthError);\n      sdk.off('logout', handleLogout);\n    };\n  }, []);\n\n  return { isConnected };\n}\n```\n\n## Login Modal (Prebuilt UI)\n\nWalletConnect의 `showQrModal`과 유사하게, `showLoginModal: true`(기본값)로 설정하면 `connect()` 호출 시 자동으로 로그인 Modal이 표시됩니다.\n\n### Prebuilt 모드 (권장)\n\n```typescript\nimport { abcWaasConnector } from '@abcwalletio/evm/wagmi';\n\nconst config = createConfig({\n  chains: [mainnet, sepolia],\n  transports: { ... },\n  connectors: [\n    abcWaasConnector({\n      sdkConfig: { ... },\n      // Modal 자동 표시 (기본값: true)\n      showLoginModal: true,\n      modalConfig: {\n        theme: 'light',       // 'light' | 'dark'\n        language: 'ko',       // 'ko' | 'en'\n        loginMethods: ['email', 'google', 'apple', 'naver', 'kakao', 'line'],\n      },\n    }),\n  ],\n});\n\n// 사용: connect() 호출 시 자동으로 Modal 표시\nfunction ConnectButton() {\n  const { connect, connectors } = useConnect();\n  const abcConnector = connectors.find(c => c.id === 'abcWaas');\n\n  return (\n    <button onClick={() => connect({ connector: abcConnector! })}>\n      지갑 연결\n    </button>\n  );\n}\n```\n\n### Modal Flow\n\n1. **로그인 화면**: 이메일/비밀번호 입력 또는 소셜 로그인 버튼\n2. **회원가입 화면**: 이메일 → 인증코드 → 비밀번호 설정\n3. **MPC 키 설정**: 지갑 비밀번호 입력 (새 키 생성 또는 기존 키 복구)\n4. **완료**: Modal 닫힘, `connect()` Promise resolve\n\n### OAuth Callback 설정\n\n소셜 로그인을 사용하려면 OAuth callback 페이지가 필요합니다.\n\n```html\n<!-- public/oauth-callback.html -->\n<!DOCTYPE html>\n<html>\n<head><title>OAuth Callback</title></head>\n<body>\n<script>\n  (function() {\n    const params = new URLSearchParams(window.location.search);\n    const provider = localStorage.getItem('abc-waas-oauth-provider');\n\n    if (window.opener) {\n      // 팝업 모드\n      window.opener.postMessage({\n        type: 'oauth-callback',\n        provider: provider,\n        code: params.get('code'),\n        state: params.get('state'),\n        id_token: params.get('id_token'),\n      }, window.location.origin);\n      window.close();\n    } else {\n      // 리다이렉트 폴백 - 메인 페이지로 이동\n      const pending = sessionStorage.getItem('abc-waas-oauth-pending');\n      if (pending) {\n        const { returnUrl } = JSON.parse(pending);\n        sessionStorage.setItem('abc-waas-oauth-result', JSON.stringify({\n          type: 'oauth-callback',\n          provider: provider,\n          code: params.get('code'),\n        }));\n        sessionStorage.removeItem('abc-waas-oauth-pending');\n        window.location.href = returnUrl.split('?')[0];\n      }\n    }\n  })();\n</script>\n</body>\n</html>\n```\n\n### Headless 모드\n\n직접 로그인 UI를 구현하려면 `showLoginModal: false`로 설정하고 콜백을 제공합니다.\n\n```typescript\nconst connector = abcWaasConnector({\n  sdkConfig: { ... },\n  showLoginModal: false,\n  onLoginRequired: (resolve) => {\n    // 커스텀 로그인 UI 표시\n    showMyLoginUI().then(() => {\n      resolve(); // 로그인 완료 시 호출\n    });\n  },\n  onMpcKeyRequired: (resolve) => {\n    // 커스텀 MPC 키 설정 UI 표시\n    showMyMpcSetupUI().then(() => {\n      resolve(); // MPC 키 설정 완료 시 호출\n    });\n  },\n});\n```\n\n### 테마 및 언어 변경\n\n런타임에 테마/언어를 변경할 수 있습니다.\n\n```typescript\nimport { setModalTheme, setModalLanguage } from '@abcwalletio/evm/wagmi';\n\n// 다크 모드 전환\nsetModalTheme('dark');\n\n// 영어로 전환\nsetModalLanguage('en');\n```\n\n## Authentication Flow\n\nABC Waas 커넥터는 MPC 기반이므로 연결 전에 인증과 키 생성이 필요합니다.\n\n```typescript\n// 1. SDK 초기화\nconst sdk = await createWaasSDK({ ... });\n\n// 2. 인증 수행\n//    - V2: 이메일/비번 (Secure Channel)\nawait sdk.auth.v2.loginWithEmail('user@example.com', 'password');\n//    - V3: createWaasSDK({ auth: { tokenProvider } })로 초기화된 경우\n//      최초 API 호출 시 자동으로 tokenProvider가 불린다.\n\n// 3. MPC 키 생성 (최초 1회)\nconst keyResult = await sdk.mpc.generateKeyShare('secp256k1', 'password');\n\n// 또는 기존 키 복구\n// const keyResult = await sdk.mpc.recoverKeyShare('secp256k1', 'password');\n\n// 4. Wagmi에서 연결\nconst connector = abcWaasConnector({ sdk });\nawait connector.connect();\n```\n\n## TypeScript Types\n\n```typescript\nimport type {\n  // Connector\n  AbcWaasConnectorConfig,\n  ConnectorState,\n\n  // Provider\n  AbcWaasProviderConfig,\n  EIP1193Provider,\n  RequestArguments,\n  ProviderRpcError,\n  ProviderEvents,\n  ProviderEventName,\n\n  // Transaction\n  TransactionRequest,\n  TypedData,\n  TypedDataDomain,\n  TypedDataField,\n\n  // Utility\n  Address,\n  Hex,\n} from '@abcwalletio/evm/wagmi';\n```\n\n## useAbcWaas Hook\n\nReact 컴포넌트에서 ABC WaaS SDK 기능을 쉽게 사용할 수 있는 훅입니다.\n**V2 / V3 인증 경로를 모두 지원**합니다 — `setAbcWaasConfig`로 원하는 경로를 설정하세요.\n\n```tsx\nimport { setAbcWaasConfig } from '@abcwalletio/evm/wagmi';\n\n// V2 — Secure Channel\nsetAbcWaasConfig({\n  v2: { clientId: '...', clientSecret: '...' },\n});\n\n// V3 — TokenProvider 주입 (고객사 백엔드가 WaaS 토큰 발급)\nsetAbcWaasConfig({\n  v3: {\n    tokenProvider: async () => {\n      const r = await fetch('/api/waas/token', { credentials: 'include' });\n      const { access_token, expires_in } = await r.json();\n      return { accessToken: access_token, expiresIn: expires_in };\n    },\n  },\n});\n```\n\n> **V3 환경에서는** 훅의 `loginWithEmail`/`register`/`resetPassword` 등 V2 전용\n> 메서드를 호출할 필요가 없습니다. 고객사 자체 인증이 이미 수행되어 `tokenProvider`가\n> 토큰을 공급하므로, `generateMpcKey`/`recoverMpcKey`/`connectWallet`/`logout`만 사용하면 됩니다.\n\n### 기본 사용법\n\n```tsx\nimport { useAbcWaas } from '@abcwalletio/evm/wagmi';\n\nfunction MyComponent() {\n  const {\n    sdk,\n    isLoading,\n    isAuthenticated,\n    hasMpcKey,\n    error,\n\n    // 인증\n    loginWithGoogle,\n    loginWithEmail,\n    logout,\n\n    // 회원가입\n    checkEmail,\n    sendCode,\n    verifyCode,\n    register,\n\n    // 비밀번호 재설정\n    sendResetCode,\n    resetPassword,\n\n    // MPC 키\n    generateMpcKey,\n    recoverMpcKey,\n\n    // 지갑 연결\n    connectWallet,\n    disconnectWallet,\n  } = useAbcWaas();\n\n  return (\n    <div>\n      {isLoading && <p>로딩 중...</p>}\n      {isAuthenticated ? (\n        <button onClick={logout}>로그아웃</button>\n      ) : (\n        <button onClick={() => loginWithEmail('user@example.com', 'password')}>\n          로그인\n        </button>\n      )}\n    </div>\n  );\n}\n```\n\n### 반환값\n\n| 속성 | 타입 | 설명 |\n|------|------|------|\n| `sdk` | `WaasSDK \\| null` | SDK 인스턴스 |\n| `isLoading` | `boolean` | 로딩 상태 |\n| `isAuthenticated` | `boolean` | 인증 여부 |\n| `hasMpcKey` | `boolean` | MPC 키 보유 여부 |\n| `error` | `string \\| null` | 에러 메시지 |\n| `authMode` | `AuthMode` | 현재 인증 모드 (`'idle'` \\| `'login'` \\| `'register'` \\| `'reset-password'`) |\n| `verificationStep` | `VerificationStep` | 인증 단계 (`'email'` \\| `'code'` \\| `'password'` \\| `'complete'`) |\n\n### 메서드\n\n| 메서드 | 설명 |\n|--------|------|\n| `initializeSDK()` | SDK 초기화 |\n| `loginWithGoogle(token, email)` | Google 로그인 |\n| `loginWithEmail(email, password)` | 이메일 로그인 |\n| `checkEmail(email)` | 이메일 가용성 확인 |\n| `sendCode(email)` | 인증코드 발송 |\n| `verifyCode(email, code)` | 인증코드 검증 |\n| `register(email, password, code)` | 회원가입 |\n| `sendResetCode(email)` | 비밀번호 재설정 코드 발송 |\n| `resetPassword(email, code, newPassword)` | 비밀번호 재설정 |\n| `generateMpcKey(password)` | MPC 키 생성 |\n| `recoverMpcKey(password)` | MPC 키 복구 |\n| `connectWallet()` | 지갑 연결 |\n| `disconnectWallet()` | 지갑 연결 해제 |\n| `logout()` | 로그아웃 |\n\n## Store API\n\n커넥터의 상태를 직접 관리하거나 구독할 수 있습니다.\n\n```typescript\nimport {\n  getState,\n  subscribe,\n  updateState,\n  initializeState,\n  resetState,\n  bindSdkEvents,\n  // Modal 관련\n  showModal,\n  hideModal,\n  setModalView,\n  setModalLanguage,\n  setModalTheme,\n} from '@abcwalletio/evm/wagmi';\n\n// 현재 상태 가져오기\nconst state = getState();\nconsole.log(state.isAuthenticated, state.hasMpcKey);\n\n// 상태 변경 구독\nconst unsubscribe = subscribe(() => {\n  const newState = getState();\n  console.log('State changed:', newState);\n});\n\n// 구독 해제\nunsubscribe();\n\n// Modal 테마/언어 변경\nsetModalTheme('dark');\nsetModalLanguage('en');\n\n// Modal 표시/숨김\nshowModal('login');\nhideModal();\n```\n\n### AbcWaasState\n\n```typescript\ninterface AbcWaasState {\n  sdk: WaasSDK | null;\n  isAuthenticated: boolean;\n  hasMpcKey: boolean;\n  isLoading: boolean;\n  error: string | null;\n  modalView: ModalView;\n  modalConfig: ModalConfig | null;\n  modalResolve: (() => void) | null;\n  pendingEmail: string | null;\n  pendingVerificationCode: string | null;\n}\n```\n\n## Modal Components\n\n로그인 Modal을 직접 마운트하거나 커스텀 UI에 활용할 수 있습니다.\n\n### Modal 마운트/언마운트\n\n```typescript\nimport { mountModal, unmountModal, isModalMounted } from '@abcwalletio/evm/wagmi';\n\n// Modal을 DOM에 마운트 (앱 초기화 시)\nmountModal();\n\n// Modal 마운트 상태 확인\nif (isModalMounted()) {\n  console.log('Modal is ready');\n}\n\n// Modal 언마운트 (앱 종료 시)\nunmountModal();\n```\n\n### UI 컴포넌트\n\n```typescript\nimport {\n  AbcWaasModal,    // 전체 Modal 컴포넌트\n  ModalHeader,      // Modal 헤더\n  LanguageToggle,   // 언어 전환 토글\n  LoadingView,      // 로딩 화면\n  ErrorView,        // 에러 표시 화면\n} from '@abcwalletio/evm/wagmi';\n```\n\n### 스타일 유틸리티\n\n```typescript\nimport {\n  getModalStyles,\n  getButtonStyles,\n  getInputStyles,\n  getLabelStyles,\n  getErrorStyles,\n  getLinkStyles,\n} from '@abcwalletio/evm/wagmi';\n\n// 테마별 스타일 가져오기\nconst modalStyles = getModalStyles('dark');\nconst buttonStyles = getButtonStyles('primary', 'dark');\n```\n\n## i18n (다국어 지원)\n\nModal 텍스트를 커스터마이징하거나 다국어 지원에 활용할 수 있습니다.\n\n```typescript\nimport { i18n } from '@abcwalletio/evm/wagmi';\n\n// 번역 텍스트 가져오기\nconst loginTexts = i18n.login;    // 로그인 관련 텍스트\nconst registerTexts = i18n.register;  // 회원가입 관련 텍스트\nconst mpcTexts = i18n.mpc;        // MPC 키 설정 관련 텍스트\nconst errorTexts = i18n.error;    // 에러 메시지\nconst commonTexts = i18n.common;  // 공통 텍스트\n\n// 번역 헬퍼 함수\nconst text = i18n.t(i18n.login, 'ko');  // 한국어 로그인 텍스트\nconst textEn = i18n.t(i18n.login, 'en'); // 영어 로그인 텍스트\n```\n\n### 지원 언어\n\n- `ko`: 한국어 (기본값)\n- `en`: 영어\n\n## Constants\n\n```typescript\nimport { CONNECTOR_ID, CONNECTOR_NAME, getAbcWaasSDK } from '@abcwalletio/evm/wagmi';\n\n// Connector ID (Wagmi에서 커넥터 찾을 때 사용)\nconsole.log(CONNECTOR_ID);  // 'abcWaas'\n\n// Connector 표시 이름\nconsole.log(CONNECTOR_NAME);  // 'ABC Waas'\n\n// 현재 연결된 SDK 인스턴스 가져오기\nconst sdk = getAbcWaasSDK();\n```\n\n## License\n\nMIT — Copyright (c) 2026 Ahnlab Blockchain Company\n","readmeFilename":"README.md","_rev":"1-d644f428654add533f03d16cf5d883bc"}