@gluwa/connect-kit 0.1.0-next.1 → 0.1.0-next.3
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/CHANGELOG.md +12 -0
- package/dist/chunk-DDA6FP6U.js +7 -0
- package/dist/credit-connect.d.ts +16 -0
- package/dist/credit-connect.js +399 -0
- package/dist/index.d.ts +55 -15
- package/dist/index.js +348 -432
- package/dist/package.json +13 -1
- package/package.json +10 -1
- package/src/ConnectKitProvider.tsx +198 -0
- package/src/ConnectModal.scss +40 -2
- package/src/ConnectModal.tsx +193 -61
- package/src/credit-connect.ts +2 -0
- package/src/creditConnectConnector.ts +1 -0
- package/src/index.ts +4 -2
- package/src/types.ts +14 -0
- package/src/views/CreditWalletView.tsx +5 -2
- package/src/views/SwitchChainView.tsx +67 -0
- package/tsup.config.ts +5 -1
package/dist/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gluwa/connect-kit",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"default": "./src/index.ts"
|
|
8
8
|
},
|
|
9
|
+
"./credit-connect": {
|
|
10
|
+
"default": "./src/credit-connect.ts"
|
|
11
|
+
},
|
|
9
12
|
"./package.json": "./package.json"
|
|
10
13
|
},
|
|
11
14
|
"publishConfig": {
|
|
@@ -17,6 +20,10 @@
|
|
|
17
20
|
"types": "./dist/index.d.ts",
|
|
18
21
|
"default": "./dist/index.js"
|
|
19
22
|
},
|
|
23
|
+
"./credit-connect": {
|
|
24
|
+
"types": "./dist/credit-connect.d.ts",
|
|
25
|
+
"default": "./dist/credit-connect.js"
|
|
26
|
+
},
|
|
20
27
|
"./index.css": "./dist/index.css"
|
|
21
28
|
}
|
|
22
29
|
},
|
|
@@ -35,6 +42,7 @@
|
|
|
35
42
|
},
|
|
36
43
|
"devDependencies": {
|
|
37
44
|
"@gluwa/credit-connect-sdk": "workspace:*",
|
|
45
|
+
"@tanstack/react-query": "^5.62.0",
|
|
38
46
|
"@types/react": "catalog:react18",
|
|
39
47
|
"@wagmi/core": "^2.16.7",
|
|
40
48
|
"esbuild-sass-plugin": "^3.7.0",
|
|
@@ -48,6 +56,7 @@
|
|
|
48
56
|
},
|
|
49
57
|
"peerDependencies": {
|
|
50
58
|
"@gluwa/credit-connect-sdk": "workspace:*",
|
|
59
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
51
60
|
"@wagmi/core": ">=2.0.0",
|
|
52
61
|
"react": "^18.0.0 || ^19.0.0",
|
|
53
62
|
"viem": ">=2.0.0",
|
|
@@ -56,6 +65,9 @@
|
|
|
56
65
|
"peerDependenciesMeta": {
|
|
57
66
|
"@gluwa/credit-connect-sdk": {
|
|
58
67
|
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"@tanstack/react-query": {
|
|
70
|
+
"optional": true
|
|
59
71
|
}
|
|
60
72
|
},
|
|
61
73
|
"repository": {
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gluwa/connect-kit",
|
|
3
|
-
"version": "0.1.0-next.
|
|
3
|
+
"version": "0.1.0-next.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"default": "./dist/index.js"
|
|
9
9
|
},
|
|
10
|
+
"./credit-connect": {
|
|
11
|
+
"types": "./dist/credit-connect.d.ts",
|
|
12
|
+
"default": "./dist/credit-connect.js"
|
|
13
|
+
},
|
|
10
14
|
"./index.css": "./dist/index.css"
|
|
11
15
|
},
|
|
12
16
|
"publishConfig": {
|
|
@@ -19,6 +23,7 @@
|
|
|
19
23
|
"qrcode.react": "^3.2.0"
|
|
20
24
|
},
|
|
21
25
|
"devDependencies": {
|
|
26
|
+
"@tanstack/react-query": "^5.62.0",
|
|
22
27
|
"@types/react": "^18.3.18",
|
|
23
28
|
"@wagmi/core": "^2.16.7",
|
|
24
29
|
"esbuild-sass-plugin": "^3.7.0",
|
|
@@ -32,6 +37,7 @@
|
|
|
32
37
|
"@gluwa/credit-connect-sdk": "0.2.0-next.1"
|
|
33
38
|
},
|
|
34
39
|
"peerDependencies": {
|
|
40
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
35
41
|
"@wagmi/core": ">=2.0.0",
|
|
36
42
|
"react": "^18.0.0 || ^19.0.0",
|
|
37
43
|
"viem": ">=2.0.0",
|
|
@@ -41,6 +47,9 @@
|
|
|
41
47
|
"peerDependenciesMeta": {
|
|
42
48
|
"@gluwa/credit-connect-sdk": {
|
|
43
49
|
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"@tanstack/react-query": {
|
|
52
|
+
"optional": true
|
|
44
53
|
}
|
|
45
54
|
},
|
|
46
55
|
"repository": {
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useCallback,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
type FC,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { WagmiProvider, useAccount, useDisconnect } from 'wagmi';
|
|
13
|
+
import type { Config } from 'wagmi';
|
|
14
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
15
|
+
import { ConnectModal } from './ConnectModal';
|
|
16
|
+
import type { ConnectErrorContext, ConnectResult, ConnectorId, Connectors } from './types';
|
|
17
|
+
|
|
18
|
+
// wagmiConfig prop으로 기존 프로젝트 통합 시 내부에서 사용하는 QueryClient.
|
|
19
|
+
// 모듈 레벨에 한 번만 생성 — 연결 플로우용으로만 쓰이므로 공유해도 무방.
|
|
20
|
+
const internalQueryClient = new QueryClient();
|
|
21
|
+
|
|
22
|
+
export interface ConnectKitProviderProps {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
connectors: Connectors;
|
|
25
|
+
wcProjectId?: string;
|
|
26
|
+
/**
|
|
27
|
+
* 연결돼 있는 동안 강제할 체인 ID. 다른 체인에 연결돼 있으면
|
|
28
|
+
* 자동으로 스위치 모달이 떠서 사용자에게 전환 또는 disconnect를 요구.
|
|
29
|
+
*/
|
|
30
|
+
requiredChainId?: number;
|
|
31
|
+
/**
|
|
32
|
+
* 연결이 정상적으로 끝나고(체인 검증까지 통과) "사용 가능한 상태"가 됐을 때 한 번 호출.
|
|
33
|
+
* 재연결(reconnect)에서는 호출되지 않음 — 그쪽은 dApp의 watchAccount 책임.
|
|
34
|
+
*/
|
|
35
|
+
onConnect?: (result: ConnectResult) => void;
|
|
36
|
+
onLog?: (message: string, error?: Error) => void;
|
|
37
|
+
/**
|
|
38
|
+
* 연결 실패(거절/미설치/기타) 시 모달 안에 표시할 fallback UI를 dApp이 직접 그리고 싶을 때.
|
|
39
|
+
* 미제공 시 기본 동작은 selector로 복귀.
|
|
40
|
+
*/
|
|
41
|
+
renderConnectError?: (ctx: ConnectErrorContext) => ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* 기존 프로젝트 통합용. wagmi config를 넘기면 ConnectKitProvider 내부에서
|
|
44
|
+
* WagmiProvider를 직접 세운다.
|
|
45
|
+
*
|
|
46
|
+
* - 기존 프로젝트: 앱에 WagmiProvider가 없으므로 wagmiConfig를 넘긴다.
|
|
47
|
+
* ConnectKitProvider가 같은 config 인스턴스로 WagmiProvider를 셀프 마운트하므로
|
|
48
|
+
* 기존 wagmi 상태 store와 단일 config를 공유한다.
|
|
49
|
+
* - 신규 프로젝트: 앱이 이미 WagmiProvider를 감싸므로 이 prop은 불필요하다.
|
|
50
|
+
*/
|
|
51
|
+
wagmiConfig?: Config;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface ConnectKitContextValue {
|
|
55
|
+
isOpen: boolean;
|
|
56
|
+
open: () => void;
|
|
57
|
+
close: () => void;
|
|
58
|
+
isConnected: boolean;
|
|
59
|
+
address: `0x${string}` | undefined;
|
|
60
|
+
connectorId: ConnectorId | null;
|
|
61
|
+
disconnect: () => Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const ConnectKitContext = createContext<ConnectKitContextValue | null>(null);
|
|
65
|
+
|
|
66
|
+
const resolveConnectorId = (wagmiConnectorId: string | undefined): ConnectorId | null => {
|
|
67
|
+
if (!wagmiConnectorId) return null;
|
|
68
|
+
if (wagmiConnectorId === 'credit-connect') return 'CREDIT_CONNECT';
|
|
69
|
+
if (wagmiConnectorId === 'walletConnect') return 'WALLET_CONNECT';
|
|
70
|
+
const lower = wagmiConnectorId.toLowerCase();
|
|
71
|
+
if (lower.includes('metamask') || wagmiConnectorId === 'injected') return 'METAMASK';
|
|
72
|
+
if (lower.includes('walletconnect')) return 'WALLET_CONNECT';
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const ConnectKitProvider: FC<ConnectKitProviderProps> = (props) => {
|
|
77
|
+
if (props.wagmiConfig) {
|
|
78
|
+
return (
|
|
79
|
+
<WagmiProvider config={props.wagmiConfig}>
|
|
80
|
+
<QueryClientProvider client={internalQueryClient}>
|
|
81
|
+
<ConnectKitProviderInner {...props} />
|
|
82
|
+
</QueryClientProvider>
|
|
83
|
+
</WagmiProvider>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return <ConnectKitProviderInner {...props} />;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const ConnectKitProviderInner: FC<ConnectKitProviderProps> = ({
|
|
90
|
+
children,
|
|
91
|
+
connectors,
|
|
92
|
+
wcProjectId,
|
|
93
|
+
requiredChainId,
|
|
94
|
+
onConnect,
|
|
95
|
+
onLog,
|
|
96
|
+
renderConnectError,
|
|
97
|
+
}) => {
|
|
98
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
99
|
+
const [pendingResult, setPendingResult] = useState<ConnectResult | null>(null);
|
|
100
|
+
|
|
101
|
+
const account = useAccount();
|
|
102
|
+
const { disconnectAsync } = useDisconnect();
|
|
103
|
+
|
|
104
|
+
// dApp의 onConnect는 ref로 잡아 effect 재실행 노이즈 차단
|
|
105
|
+
const onConnectRef = useRef(onConnect);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
onConnectRef.current = onConnect;
|
|
108
|
+
}, [onConnect]);
|
|
109
|
+
|
|
110
|
+
// chain mismatch invariant: 연결돼 있지만 요구 체인이 아니면 modal force-open
|
|
111
|
+
const chainMismatch =
|
|
112
|
+
requiredChainId != null &&
|
|
113
|
+
account.status === 'connected' &&
|
|
114
|
+
account.chainId != null &&
|
|
115
|
+
account.chainId !== requiredChainId;
|
|
116
|
+
|
|
117
|
+
const effectiveOpen = isOpen || chainMismatch;
|
|
118
|
+
|
|
119
|
+
const open = useCallback((): void => {
|
|
120
|
+
setIsOpen(true);
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
const close = useCallback((): void => {
|
|
124
|
+
setIsOpen(false);
|
|
125
|
+
}, []);
|
|
126
|
+
|
|
127
|
+
const disconnect = useCallback(async (): Promise<void> => {
|
|
128
|
+
await disconnectAsync();
|
|
129
|
+
}, [disconnectAsync]);
|
|
130
|
+
|
|
131
|
+
// ConnectModal → Provider: 연결 결과를 일단 보류했다가
|
|
132
|
+
// chain 검증까지 통과한 후에만 dApp으로 forward
|
|
133
|
+
const handleModalConnect = useCallback((result: ConnectResult): void => {
|
|
134
|
+
setPendingResult(result);
|
|
135
|
+
}, []);
|
|
136
|
+
|
|
137
|
+
// pendingResult가 있고, 연결 + chain 매칭이 충족되면 dApp으로 발화
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (!pendingResult) return;
|
|
140
|
+
if (account.status !== 'connected') {
|
|
141
|
+
// 연결이 끊겼거나 진행 중 — pending 폐기
|
|
142
|
+
setPendingResult(null);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (requiredChainId != null && account.chainId !== requiredChainId) {
|
|
146
|
+
// chain 아직 안 맞음 — switch 후 다시 평가
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
onConnectRef.current?.(pendingResult);
|
|
150
|
+
setPendingResult(null);
|
|
151
|
+
setIsOpen(false);
|
|
152
|
+
}, [pendingResult, account.status, account.chainId, requiredChainId]);
|
|
153
|
+
|
|
154
|
+
const value = useMemo<ConnectKitContextValue>(
|
|
155
|
+
() => ({
|
|
156
|
+
isOpen: effectiveOpen,
|
|
157
|
+
open,
|
|
158
|
+
close,
|
|
159
|
+
isConnected: account.status === 'connected',
|
|
160
|
+
address: account.address,
|
|
161
|
+
connectorId: resolveConnectorId(account.connector?.id),
|
|
162
|
+
disconnect,
|
|
163
|
+
}),
|
|
164
|
+
[
|
|
165
|
+
effectiveOpen,
|
|
166
|
+
open,
|
|
167
|
+
close,
|
|
168
|
+
account.status,
|
|
169
|
+
account.address,
|
|
170
|
+
account.connector?.id,
|
|
171
|
+
disconnect,
|
|
172
|
+
],
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<ConnectKitContext.Provider value={value}>
|
|
177
|
+
{children}
|
|
178
|
+
<ConnectModal
|
|
179
|
+
open={effectiveOpen}
|
|
180
|
+
connectors={connectors}
|
|
181
|
+
wcProjectId={wcProjectId}
|
|
182
|
+
requiredChainId={requiredChainId}
|
|
183
|
+
renderConnectError={renderConnectError}
|
|
184
|
+
onConnect={handleModalConnect}
|
|
185
|
+
onClose={close}
|
|
186
|
+
onLog={onLog}
|
|
187
|
+
/>
|
|
188
|
+
</ConnectKitContext.Provider>
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const useConnectKit = (): ConnectKitContextValue => {
|
|
193
|
+
const ctx = useContext(ConnectKitContext);
|
|
194
|
+
if (!ctx) {
|
|
195
|
+
throw new Error('useConnectKit must be used within a ConnectKitProvider');
|
|
196
|
+
}
|
|
197
|
+
return ctx;
|
|
198
|
+
};
|
package/src/ConnectModal.scss
CHANGED
|
@@ -140,6 +140,44 @@
|
|
|
140
140
|
&:hover {
|
|
141
141
|
background: rgba(255, 255, 255, 0.22);
|
|
142
142
|
}
|
|
143
|
+
|
|
144
|
+
&:disabled {
|
|
145
|
+
opacity: 0.6;
|
|
146
|
+
cursor: not-allowed;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.ck-btn-secondary {
|
|
151
|
+
display: inline-flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
padding: 8px 16px;
|
|
155
|
+
border-radius: 8px;
|
|
156
|
+
font-size: 14px;
|
|
157
|
+
font-weight: 500;
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
white-space: nowrap;
|
|
160
|
+
color: rgba(255, 255, 255, 0.75);
|
|
161
|
+
background: transparent;
|
|
162
|
+
border: 1px solid rgba(255, 255, 255, 0.18);
|
|
163
|
+
|
|
164
|
+
&:hover {
|
|
165
|
+
color: #fff;
|
|
166
|
+
border-color: rgba(255, 255, 255, 0.32);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.ck-view__actions {
|
|
171
|
+
display: flex;
|
|
172
|
+
flex-direction: column;
|
|
173
|
+
gap: 8px;
|
|
174
|
+
width: 100%;
|
|
175
|
+
margin-top: 16px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.ck-view__caption--error {
|
|
179
|
+
color: #ff8080;
|
|
180
|
+
margin-top: 8px;
|
|
143
181
|
}
|
|
144
182
|
|
|
145
183
|
// ── Connector list ────────────────────────────────────────
|
|
@@ -482,11 +520,11 @@
|
|
|
482
520
|
}
|
|
483
521
|
}
|
|
484
522
|
|
|
485
|
-
// 스크롤 wrapper —
|
|
523
|
+
// 스크롤 wrapper — 세로 스크롤만 담당
|
|
486
524
|
.ck-wallet-grid-scroll {
|
|
487
525
|
flex: 1;
|
|
488
526
|
min-height: 0;
|
|
489
|
-
width:
|
|
527
|
+
width: 100%;
|
|
490
528
|
overflow-x: hidden;
|
|
491
529
|
overflow-y: auto;
|
|
492
530
|
}
|