@nexus-cross/crossx-sdk-react 2.0.2-beta.2 → 2.0.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.
Files changed (2) hide show
  1. package/README.md +139 -292
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,81 +1,45 @@
1
1
  # @nexus-cross/crossx-sdk-react
2
2
 
3
- CROSSx Embedded Wallet을 React 앱에 통합하기 위한 Provider + Hooks 패키지.
3
+ React hooks and provider for integrating the CROSSx Embedded Wallet into React applications.
4
4
 
5
- > **wagmi 사용하는 경우** `@nexus-cross/crossx-sdk-wagmi`를 대신 사용하세요.
6
- > 패키지는 wagmi 없이 순수 React만으로 CROSSx 지갑을 사용할 적합합니다.
5
+ > **Using wagmi?** Use [`@nexus-cross/crossx-sdk-wagmi`](https://www.npmjs.com/package/@nexus-cross/crossx-sdk-wagmi) instead.
6
+ > This package is for apps that use CROSSx wallet exclusively, without wagmi.
7
7
 
8
- ---
9
-
10
- ## 패키지 구조
11
-
12
- ```
13
- @nexus-cross/crossx-sdk-core ← 핵심 SDK (인증, 서명, 트랜잭션)
14
- @nexus-cross/crossx-sdk-react ← React Hooks & Provider (이 패키지)
15
- @nexus-cross/crossx-sdk-wagmi ← wagmi Connector (wagmi 사용 시)
16
- ```
17
-
18
- ---
19
-
20
- ## 설치
8
+ ## Installation
21
9
 
22
10
  ```bash
23
- # pnpm (monorepo 내부)
24
- pnpm add @nexus-cross/crossx-sdk-react
25
-
26
- # npm
27
11
  npm install @nexus-cross/crossx-sdk-react
12
+ # or
13
+ pnpm add @nexus-cross/crossx-sdk-react
28
14
  ```
29
15
 
30
- `peerDependencies`로 `react ^18.0.0`이 필요합니다.
31
-
32
- ---
33
-
34
- ## 환경 변수
35
-
36
- `.env` 파일에 아래 값을 설정합니다. SDK 내부에서 자동으로 읽습니다.
37
-
38
- ```bash
39
- # OAuth 서비스 URL (로그인용) — 필수
40
- VITE_OAUTH_SERVICE_URL=https://dev-cross-wallet-oauth.crosstoken.io
41
-
42
- # JWT 검증 API URL — 필수
43
- VITE_AUTH_API_URL=https://dev-cross-auth.crosstoken.io
44
-
45
- # Embedded Wallet Gateway URL (지갑 기능용) — 필수
46
- VITE_WALLET_GATEWAY_URL=https://dev-embedded-wallet-gateway.crosstoken.io/api/v1
47
- ```
48
-
49
- > URL은 `SDKConfig`에 노출하지 않습니다. 환경 변수로만 전환 가능합니다. (외부 개발사에는 위 설정을 알리지 않을 예정)
50
-
51
- ---
16
+ **Peer dependency:** `react ^18.0.0`
52
17
 
53
18
  ## Quick Start
54
19
 
55
- ### 1. Provider 설정
56
-
57
- 앱 최상위에 `CROSSxProvider`를 배치합니다. `config`에는 `SDKConfig` 객체를 전달합니다.
20
+ ### 1. Wrap your app with `CROSSxProvider`
58
21
 
59
22
  ```tsx
60
- // main.tsx
61
- import React from 'react';
62
- import ReactDOM from 'react-dom/client';
63
23
  import { CROSSxProvider } from '@nexus-cross/crossx-sdk-react';
64
- import App from './App';
65
24
 
66
- ReactDOM.createRoot(document.getElementById('root')!).render(
67
- <React.StrictMode>
68
- <CROSSxProvider config={{}}>
69
- <App />
25
+ const config = {
26
+ projectId: 'your-project-id',
27
+ appName: 'My DApp',
28
+ theme: 'light' as const,
29
+ };
30
+
31
+ function App() {
32
+ return (
33
+ <CROSSxProvider config={config}>
34
+ <YourApp />
70
35
  </CROSSxProvider>
71
- </React.StrictMode>
72
- );
36
+ );
37
+ }
73
38
  ```
74
39
 
75
- Provider가 마운트되면 내부적으로 `createCROSSxSDK(config)` → `sdk.init()`을 자동 호출합니다.
76
- 기존 세션이 있으면 자동으로 복원됩니다.
40
+ The provider automatically calls `createCROSSxSDK(config)` → `sdk.initialize()` on mount, restoring any existing session.
77
41
 
78
- ### 2. 로그인 / 로그아웃 — `useAuth`
42
+ ### 2. Sign in / Sign out — `useAuth`
79
43
 
80
44
  ```tsx
81
45
  import { useAuth } from '@nexus-cross/crossx-sdk-react';
@@ -83,20 +47,20 @@ import { useAuth } from '@nexus-cross/crossx-sdk-react';
83
47
  function LoginButton() {
84
48
  const { signIn, signOut, isAuthenticated, isLoading, error } = useAuth();
85
49
 
86
- if (isLoading) return <p>처리 중...</p>;
50
+ if (isLoading) return <p>Loading...</p>;
87
51
 
88
52
  return isAuthenticated ? (
89
- <button onClick={signOut}>로그아웃</button>
53
+ <button onClick={signOut}>Sign Out</button>
90
54
  ) : (
91
55
  <div>
92
- <button onClick={signIn}>로그인</button>
56
+ <button onClick={signIn}>Sign In</button>
93
57
  {error && <p style={{ color: 'red' }}>{error}</p>}
94
58
  </div>
95
59
  );
96
60
  }
97
61
  ```
98
62
 
99
- ### 3. 서명 / 전송 — `useWallet`
63
+ ### 3. Sign & Send — `useWallet`
100
64
 
101
65
  ```tsx
102
66
  import { useWallet } from '@nexus-cross/crossx-sdk-react';
@@ -106,38 +70,33 @@ function WalletActions() {
106
70
  const { address, signMessage, sendTransaction, isLoading, error } = useWallet();
107
71
 
108
72
  const handleSign = async () => {
109
- // chainId는 CAIP-2 형식 문자열 (예: 'eip155:612055')
110
73
  const result = await signMessage(ChainId.CROSS_MAINNET, 'Hello CROSSx!');
111
- console.log('signature:', result.signature);
74
+ console.log('Signature:', result.signature);
112
75
  };
113
76
 
114
77
  const handleSend = async () => {
115
78
  const result = await sendTransaction(ChainId.CROSS_MAINNET, {
116
79
  from: address!,
117
- to: '0x920A31f0E48739C3FbB790D992b0690f7F5C42ea',
118
- value: '0x2386f26fc10000', // 0.01 CROSS (hex wei)
119
- gasLimit: '0x5208',
120
- maxFeePerGas: '0x77359400',
121
- maxPriorityFeePerGas: '0x3b9aca00',
80
+ to: '0x...',
81
+ value: '0x2386f26fc10000',
122
82
  });
123
83
  console.log('txHash:', result.txHash);
124
84
  };
125
85
 
126
86
  return (
127
87
  <div>
128
- <p>주소: {address ?? '—'}</p>
129
- <button onClick={handleSign} disabled={isLoading}>메시지 서명</button>
130
- <button onClick={handleSend} disabled={isLoading}>전송</button>
88
+ <p>Address: {address ?? '—'}</p>
89
+ <button onClick={handleSign} disabled={isLoading}>Sign Message</button>
90
+ <button onClick={handleSend} disabled={isLoading}>Send Transaction</button>
131
91
  {error && <p style={{ color: 'red' }}>{error}</p>}
132
92
  </div>
133
93
  );
134
94
  }
135
95
  ```
136
96
 
137
- ### 4. SDK 직접 접근 — `useCROSSx`
97
+ ### 4. Direct SDK access — `useCROSSx`
138
98
 
139
- hooks로 커버되지 않는 기능(잔액 조회, nonce 조회, RPC 요청 )
140
- `useCROSSx`로 SDK 인스턴스에 직접 접근합니다.
99
+ For features not covered by hooks (balance, nonce, RPC, typed data signing, etc.), access the SDK instance directly.
141
100
 
142
101
  ```tsx
143
102
  import { useCROSSx } from '@nexus-cross/crossx-sdk-react';
@@ -148,67 +107,28 @@ function AdvancedPanel() {
148
107
  const handleGetBalance = async () => {
149
108
  if (!sdk) return;
150
109
  const { formatted } = await sdk.getBalance('eip155:612055');
151
- console.log('잔액:', formatted, 'CROSS');
110
+ console.log('Balance:', formatted);
152
111
  };
153
112
 
154
113
  const handleSignTypedData = async () => {
155
114
  if (!sdk) return;
156
115
  const result = await sdk.signTypedData('eip155:612055', {
157
- types: {
158
- EIP712Domain: [
159
- { name: 'name', type: 'string' },
160
- { name: 'version', type: 'string' },
161
- { name: 'chainId', type: 'uint256' },
162
- { name: 'verifyingContract', type: 'address' },
163
- ],
164
- Permit: [
165
- { name: 'owner', type: 'address' },
166
- { name: 'spender', type: 'address' },
167
- { name: 'value', type: 'uint256' },
168
- { name: 'nonce', type: 'uint256' },
169
- { name: 'deadline', type: 'uint256' },
170
- ],
171
- },
116
+ types: { /* EIP-712 type definitions */ },
172
117
  primaryType: 'Permit',
173
- domain: {
174
- name: 'MyToken',
175
- version: '1',
176
- chainId: 612055,
177
- verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
178
- },
179
- message: {
180
- owner: walletAddress,
181
- spender: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
182
- value: '1000000000000000000',
183
- nonce: '0',
184
- deadline: '1234567890',
185
- },
118
+ domain: { /* domain separator */ },
119
+ message: { /* typed message */ },
186
120
  });
187
- console.log('typed data signature:', result.signature);
121
+ console.log('Signature:', result.signature);
188
122
  };
189
123
 
190
- const handleRpcCall = async () => {
191
- if (!sdk) return;
192
- // eth_call로 ERC-20 balanceOf 직접 호출
193
- const data = '0x70a08231000000000000000000000000' +
194
- walletAddress!.slice(2).toLowerCase().padStart(64, '0');
195
- const raw = await sdk.walletRpc(
196
- 'eth_call',
197
- [{ to: '0x9f85c7b5d7637e18f946cc8af9c131318c6833d9', data }, 'latest'],
198
- 'eip155:612044'
199
- );
200
- console.log('balanceOf result:', raw);
201
- };
202
-
203
- if (!isInitialized) return <p>초기화 중...</p>;
124
+ if (!isInitialized) return <p>Initializing...</p>;
204
125
 
205
126
  return (
206
127
  <div>
207
- <p>인증: {isAuthenticated ? '' : ''}</p>
208
- <p>주소: {walletAddress ?? '—'}</p>
209
- <button onClick={handleGetBalance}>잔액 조회</button>
210
- <button onClick={handleSignTypedData}>EIP-712 서명</button>
211
- <button onClick={handleRpcCall}>eth_call (tUSDT)</button>
128
+ <p>Authenticated: {isAuthenticated ? 'Yes' : 'No'}</p>
129
+ <p>Address: {walletAddress ?? '—'}</p>
130
+ <button onClick={handleGetBalance}>Get Balance</button>
131
+ <button onClick={handleSignTypedData}>EIP-712 Sign</button>
212
132
  </div>
213
133
  );
214
134
  }
@@ -220,147 +140,108 @@ function AdvancedPanel() {
220
140
 
221
141
  ### `<CROSSxProvider>`
222
142
 
223
- | Prop | 타입 | 설명 |
224
- |------|------|------|
225
- | `config` | `SDKConfig` | SDK 설정 객체 |
226
- | `children` | `ReactNode` | 자식 컴포넌트 |
227
-
228
- `SDKConfig` 주요 필드:
143
+ | Prop | Type | Description |
144
+ |------|------|-------------|
145
+ | `config` | `SDKConfig` | SDK configuration object |
146
+ | `children` | `ReactNode` | Child components |
229
147
 
230
- | 필드 | 타입 | 기본값 | 설명 |
231
- |------|------|--------|------|
232
- | `theme` | `'light' \| 'dark'` | `'light'` | 확인 모달 테마 |
233
- | `themeTokens` | `ConfirmationTokenOverride` | — | 모달 색상 커스터마이징 |
234
- | `oauthDisplayMode` | `'popup' \| 'modal'` | `'popup'` | OAuth 로그인 표시 방식 |
235
- | `useMockWallet` | `boolean` | `false` | Mock 지갑 사용 (개발용) |
236
- | `debug` | `boolean` | `true` | 디버그 로그 출력 |
148
+ Key `SDKConfig` fields:
237
149
 
238
- `ConfirmationTokenOverride` 구조:
239
-
240
- ```ts
241
- themeTokens: {
242
- light?: ConfirmationThemeTokens; // 라이트 모드 오버라이드
243
- dark?: ConfirmationThemeTokens; // 다크 모드 오버라이드
244
- }
245
- ```
246
-
247
- `ConfirmationThemeTokens` 오버라이드 가능 필드:
248
-
249
- | 필드 | Semantic 토큰 | 기본값 (light / dark) |
250
- |------|------|------|
251
- | `primary` | Primary | `#019D92` / `#019D92` |
252
- | `secondary` | Secondary | `#E70077` / `#E70077` |
253
- | `onPrimary` | OnPrimary | `#FFFFFF` / `#FFFFFF` |
254
- | `borderDefault` | Border/Default | `rgba(18,18,18,0.05)` / `rgba(255,255,255,0.05)` |
255
- | `borderSubtle` | Border/Subtle | `rgba(18,18,18,0.1)` / `rgba(255,255,255,0.1)` |
256
- | `textPrimary` | TextIcon/Primary | `#121212` / `#FFFFFF` |
257
- | `textSecondary` | TextIcon/Secondary | `rgba(18,18,18,0.7)` / `rgba(255,255,255,0.7)` |
258
- | `textTertiary` | TextIcon/Tertiary | `rgba(18,18,18,0.5)` / `rgba(255,255,255,0.5)` |
259
- | `surfaceDefault` | Surface/default | `rgba(18,18,18,0.05)` / `rgba(255,255,255,0.05)` |
260
- | `bg` | Surface/BG | `#FFFFFF` / `#121212` |
150
+ | Field | Type | Default | Description |
151
+ |-------|------|---------|-------------|
152
+ | `projectId` | `string` | — | **Required.** Project ID from the management console |
153
+ | `appName` | `string` | — | **Required.** App name shown in confirmation modals |
154
+ | `theme` | `'light' \| 'dark'` | `'light'` | Confirmation modal theme |
155
+ | `autoDetectTheme` | `boolean` | `false` | Follow OS dark mode setting |
156
+ | `themeTokens` | `SDKThemeTokens` | — | Per-mode color overrides |
157
+ | `locale` | `'en' \| 'ko'` | `'en'` | Modal UI language |
158
+ | `debug` | `boolean` | `true` | Debug logging (dev builds only) |
261
159
 
262
160
  ---
263
161
 
264
162
  ### `useAuth()`
265
163
 
266
- 인증(로그인/로그아웃) 관련 상태와 액션을 제공합니다.
164
+ Authentication state and actions.
267
165
 
268
166
  ```ts
269
167
  const {
270
- isAuthenticated, // boolean — 현재 인증 상태
271
- isLoading, // boolean — 비동기 작업 진행 중
272
- error, // string | null — 마지막 에러 메시지
273
- signIn, // () => Promise<AuthResult> — OAuth 로그인
274
- signOut, // () => Promise<void> — 로그아웃
168
+ isAuthenticated, // boolean
169
+ isLoading, // boolean
170
+ error, // string | null
171
+ signIn, // () => Promise<AuthResult>
172
+ signOut, // () => Promise<void>
275
173
  } = useAuth();
276
174
  ```
277
175
 
278
- `signIn()` 호출 시 OAuth 팝업/모달이 열립니다.
279
- 성공하면 내부적으로 `window.location.reload()`가 호출되어 Provider 상태가 갱신됩니다.
280
-
281
176
  ---
282
177
 
283
178
  ### `useWallet()`
284
179
 
285
- 지갑 주소, 메시지 서명, 트랜잭션 전송 기능을 제공합니다.
180
+ Wallet address, message signing, and transaction sending.
286
181
 
287
182
  ```ts
288
183
  const {
289
- address, // string | null — 지갑 주소 (0x...)
290
- isLoading, // boolean — 비동기 작업 진행 중
291
- error, // string | null — 마지막 에러 메시지
292
- signMessage, // (chainId, message) => Promise<SignMessageResult>
293
- sendTransaction, // (chainId, tx) => Promise<TransactionResult>
184
+ address, // string | null — wallet address (0x...)
185
+ isLoading, // boolean
186
+ error, // string | null
187
+ signMessage, // (chainId, message) => Promise<SignMessageResp>
188
+ sendTransaction, // (chainId, tx) => Promise<SendTxResp>
294
189
  } = useWallet();
295
190
  ```
296
191
 
297
192
  **`signMessage(chainId, message)`**
298
193
 
299
- | 파라미터 | 타입 | 예시 |
300
- |----------|------|------|
301
- | `chainId` | `string` | `'eip155:612055'` 또는 `ChainId.CROSS_MAINNET` |
194
+ | Parameter | Type | Example |
195
+ |-----------|------|---------|
196
+ | `chainId` | `string` | `ChainId.CROSS_MAINNET` |
302
197
  | `message` | `string` | `'Hello CROSSx!'` |
303
198
 
304
- 반환: `{ chainId, signature, message, address }`
199
+ Returns: `{ chainId, signature, message, address }`
305
200
 
306
201
  **`sendTransaction(chainId, tx)`**
307
202
 
308
- | 파라미터 | 타입 | 설명 |
309
- |----------|------|------|
310
- | `chainId` | `string` | CAIP-2 체인 ID |
311
- | `tx` | `EvmTransactionRequest` | 트랜잭션 객체 |
312
-
313
- `EvmTransactionRequest` 주요 필드:
314
-
315
- | 필드 | 타입 | 필수 | 설명 |
316
- |------|------|:----:|------|
317
- | `from` | `string` | O | 보내는 주소 |
318
- | `to` | `string` | O | 받는 주소 |
319
- | `value` | `string` | | hex wei (예: `'0x2386f26fc10000'`) |
320
- | `data` | `string` | | calldata hex |
321
- | `gasLimit` | `string` | | gas limit hex |
322
- | `maxFeePerGas` | `string` | | EIP-1559 max fee |
323
- | `maxPriorityFeePerGas` | `string` | | EIP-1559 priority fee |
324
- | `nonce` | `number` | | 트랜잭션 순서 |
203
+ | Parameter | Type | Description |
204
+ |-----------|------|-------------|
205
+ | `chainId` | `string` | CAIP-2 chain ID |
206
+ | `tx` | `EvmTransactionRequest` | Transaction object |
325
207
 
326
- 반환: `{ chainId, txHash, status }`
208
+ Returns: `{ chainId, txHash, status }`
327
209
 
328
210
  ---
329
211
 
330
212
  ### `useCROSSx()`
331
213
 
332
- SDK 인스턴스와 전역 상태에 직접 접근합니다.
214
+ Direct access to the SDK instance and global state.
333
215
 
334
216
  ```ts
335
217
  const {
336
- sdk, // CROSSxSDK | null — SDK 인스턴스
337
- isInitialized, // boolean — init() 완료 여부
338
- isAuthenticated, // boolean — 인증 상태
339
- walletAddress, // string | null — 지갑 주소
218
+ sdk, // CROSSxSDK | null
219
+ isInitialized, // boolean
220
+ isAuthenticated, // boolean
221
+ walletAddress, // string | null
340
222
  } = useCROSSx();
341
223
  ```
342
224
 
343
- `sdk` 인스턴스를 통해 호출할 수 있는 주요 메서드:
344
-
345
- | 메서드 | 설명 |
346
- |--------|------|
347
- | `sdk.signMessage(chainId, message)` | EIP-191 메시지 서명 |
348
- | `sdk.signTypedData(chainId, typedData)` | EIP-712 Typed Data 서명 |
349
- | `sdk.signTransaction(chainId, tx)` | 트랜잭션 서명 (전송 없음) |
350
- | `sdk.sendTransaction(chainId, tx)` | 트랜잭션 서명 + 전송 |
351
- | `sdk.sendTransactionAndWait(chainId, tx)` | 전송 + Receipt 폴링 |
352
- | `sdk.getBalance(chainId)` | 네이티브 잔액 조회 |
353
- | `sdk.getNonce(chainId)` | 현재 nonce 조회 |
354
- | `sdk.walletRpc(method, params, chainId)` | 범용 JSON-RPC 호출 |
355
- | `sdk.getProvider(chainId)` | EIP-1193 Provider 반환 |
356
- | `sdk.createWallet()` | 수동 지갑 생성 |
357
- | `sdk.setTheme('dark')` | 확인 모달 테마 런타임 전환 (`themeTokens` 오버라이드 유지) |
225
+ Available methods via `sdk`:
226
+
227
+ | Method | Description |
228
+ |--------|-------------|
229
+ | `sdk.signMessage(chainId, msg)` | EIP-191 personal sign |
230
+ | `sdk.signTypedData(chainId, data)` | EIP-712 typed data sign |
231
+ | `sdk.signTransaction(chainId, tx)` | Sign without broadcasting |
232
+ | `sdk.sendTransaction(chainId, tx)` | Sign + broadcast |
233
+ | `sdk.sendTransactionWithWaitForReceipt(chainId, tx)` | Send + poll receipt |
234
+ | `sdk.getBalance(chainId)` | Native balance |
235
+ | `sdk.getNonce(chainId)` | Current nonce |
236
+ | `sdk.walletRpc(method, params, chainId)` | Generic JSON-RPC |
237
+ | `sdk.getProvider(chainId)` | EIP-1193 provider |
238
+ | `sdk.createWallet()` | Manual wallet creation |
239
+ | `sdk.selectWallet()` | Open wallet selector |
240
+ | `sdk.applyTheme('dark')` | Runtime theme switch |
358
241
 
359
242
  ---
360
243
 
361
- ## ChainId 상수
362
-
363
- 체인 ID 오타 방지를 위한 상수를 제공합니다.
244
+ ## Chain ID Constants
364
245
 
365
246
  ```ts
366
247
  import { ChainId } from '@nexus-cross/crossx-sdk-react';
@@ -371,100 +252,66 @@ ChainId.CROSS_TESTNET // 'eip155:612044'
371
252
 
372
253
  ---
373
254
 
374
- ## wagmi와의 차이
375
-
376
- | | `@nexus-cross/crossx-sdk-react` | `@nexus-cross/crossx-sdk-wagmi` |
377
- |---|---|---|
378
- | **의존성** | React만 필요 | wagmi + viem + @tanstack/react-query |
379
- | **접근 방식** | CROSSx 전용 hooks | wagmi 표준 hooks (`useAccount`, `useConnect` 등) |
380
- | **EIP-1193** | 필요 시 `sdk.getProvider()` | 자동 제공 (wagmi connector) |
381
- | **적합 대상** | CROSSx 지갑만 사용하는 앱 | 멀티 지갑(MetaMask 등) 지원 앱 |
382
-
383
- wagmi를 사용하는 경우 `@nexus-cross/crossx-sdk-wagmi`의 `createCROSSxConnector`를 사용하세요.
384
- wagmi 예제는 `examples/wagmi-app`을 참고하세요.
385
-
386
- ---
387
-
388
- ## 확인 모달 (Confirmation)
255
+ ## Confirmation Modal
389
256
 
390
- 서명/전송 자동으로 확인 모달이 표시됩니다.
257
+ All sign/send operations automatically display an approval modal:
391
258
 
392
- - **Message Sign** — "Signature Request" 모달 (Cancel / Confirm)
393
- - **EIP-712 Typed Data** — 구조화된 key-value 표시
394
- - **Transaction Sign/Send** — 수신자, 금액, 수수료 표시
259
+ - **Message Sign** — "Signature Request" modal
260
+ - **EIP-712 Typed Data** — Structured key-value display
261
+ - **Transaction** — Recipient, amount, and fee details
395
262
 
396
- 모바일(480px 이하)에서는 자동으로 하단 바텀시트로 전환됩니다.
263
+ On mobile (480px), the modal automatically renders as a bottom sheet.
397
264
 
398
- ### 테마 설정
265
+ ### Theme Customization
399
266
 
400
- **초기화 시 고정:**
401
267
  ```tsx
268
+ // At initialization
402
269
  <CROSSxProvider config={{ theme: 'dark' }}>
403
- ```
404
270
 
405
- **런타임 전환:**
406
- ```ts
271
+ // Runtime switch
407
272
  const { sdk } = useCROSSx();
408
- sdk?.setTheme('dark'); // 다음 모달부터 적용. themeTokens 오버라이드는 유지됨
273
+ sdk?.applyTheme('dark');
274
+
275
+ // Custom colors
276
+ <CROSSxProvider config={{
277
+ theme: 'light',
278
+ themeTokens: {
279
+ light: { primary: '#FF6B35', bg: '#F5F0EB' },
280
+ dark: { primary: '#FF6B35', bg: '#1A0A00' },
281
+ },
282
+ }}>
409
283
  ```
410
284
 
411
- ### 색상 커스터마이징
412
-
413
- `themeTokens.light` / `themeTokens.dark` 로 각 모드를 독립적으로 오버라이드합니다.
414
- 지정하지 않은 항목은 해당 모드의 기본값을 유지합니다.
415
-
416
- ```tsx
417
- <CROSSxProvider
418
- config={{
419
- theme: 'light',
420
- themeTokens: {
421
- light: {
422
- primary: '#FF6B35', // 버튼·강조색 (기본: #019D92)
423
- bg: '#F5F0EB', // 카드 배경색 (기본: #FFFFFF)
424
- },
425
- dark: {
426
- primary: '#FF6B35',
427
- bg: '#1A0A00', // 카드 배경색 (기본: #121212)
428
- },
429
- },
430
- }}
431
- >
432
- ```
285
+ ---
433
286
 
434
- 특정 모드만 지정하는 것도 가능합니다.
287
+ ## Comparison with wagmi Package
435
288
 
436
- ```tsx
437
- // 라이트 모드만 커스터마이징
438
- themeTokens: { light: { primary: '#FF6B35' } }
439
- ```
289
+ | | `@nexus-cross/crossx-sdk-react` | `@nexus-cross/crossx-sdk-wagmi` |
290
+ |---|---|---|
291
+ | **Dependencies** | React only | wagmi + viem + @tanstack/react-query |
292
+ | **Approach** | CROSSx-specific hooks | wagmi standard hooks (`useAccount`, etc.) |
293
+ | **EIP-1193** | Via `sdk.getProvider()` | Automatic (wagmi connector) |
294
+ | **Best for** | CROSSx-only apps | Multi-wallet apps (MetaMask, etc.) |
440
295
 
441
296
  ---
442
297
 
443
- ## 마이그레이션 (자동)
298
+ ## Troubleshooting
444
299
 
445
- 이전 CROSSx 네이티브 사용자가 로그인하면,
446
- 기존 백업이 감지될 경우 PIN 입력 팝업이 자동으로 표시되어
447
- Embedded Wallet로 마이그레이션됩니다.
300
+ **`useCROSSx must be used within CROSSxProvider`**
301
+ Wrap your app root with `<CROSSxProvider>`.
448
302
 
449
- 별도 코드 작성이 필요 없습니다.
303
+ **OAuth popup doesn't open**
304
+ Check browser popup blocker, or use `config.oauthDisplayMode = 'modal'`.
450
305
 
451
306
  ---
452
307
 
453
- ## 트러블슈팅
454
-
455
- ### `useCROSSx must be used within CROSSxProvider`
456
- 앱 최상위에 `<CROSSxProvider>`가 빠져있습니다.
308
+ ## Related Packages
457
309
 
458
- ### OAuth 팝업이 열리지 않음
459
- 브라우저의 팝업 차단을 확인하세요.
460
- 또는 `config.oauthDisplayMode = 'modal'`로 변경하세요.
461
-
462
- ### 로그인 후 상태가 업데이트되지 않음
463
- 현재 `useAuth().signIn()`은 성공 시 `window.location.reload()`를 호출합니다.
464
- SPA 라우터와 충돌하는 경우 `useCROSSx()`로 직접 상태를 관리하세요.
465
-
466
- ---
310
+ | Package | Description |
311
+ |---------|-------------|
312
+ | [`@nexus-cross/crossx-sdk-core`](https://www.npmjs.com/package/@nexus-cross/crossx-sdk-core) | Core SDK (vanilla JS) |
313
+ | [`@nexus-cross/crossx-sdk-wagmi`](https://www.npmjs.com/package/@nexus-cross/crossx-sdk-wagmi) | wagmi connector |
467
314
 
468
- ## 라이센스
315
+ ## License
469
316
 
470
317
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexus-cross/crossx-sdk-react",
3
- "version": "2.0.2-beta.2",
3
+ "version": "2.0.2",
4
4
  "description": "CROSSx React SDK - React Hooks and Components for Embedded Wallet",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -33,7 +33,7 @@
33
33
  "react": "^18.0.0"
34
34
  },
35
35
  "dependencies": {
36
- "@nexus-cross/crossx-sdk-core": "2.0.2-beta.2"
36
+ "@nexus-cross/crossx-sdk-core": "2.0.2"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "^18.2.48",