@zerodev/wallet-react 0.0.1-alpha.0 → 0.0.1-alpha.10
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 +73 -0
- package/README.md +282 -0
- package/dist/_cjs/actions.js +62 -24
- package/dist/_cjs/connector.js +86 -52
- package/dist/_cjs/constants.js +1 -1
- package/dist/_cjs/hooks/useExportPrivateKey.js +18 -0
- package/dist/_cjs/hooks/useGetUserEmail.js +19 -0
- package/dist/_cjs/index.js +8 -1
- package/dist/_cjs/oauth.js +60 -55
- package/dist/_cjs/provider.js +5 -2
- package/dist/_cjs/store.js +4 -9
- package/dist/_cjs/utils/aaUtils.js +2 -2
- package/dist/_esm/actions.js +74 -27
- package/dist/_esm/connector.js +102 -59
- package/dist/_esm/constants.js +1 -1
- package/dist/_esm/hooks/useExportPrivateKey.js +18 -0
- package/dist/_esm/hooks/useGetUserEmail.js +19 -0
- package/dist/_esm/index.js +3 -1
- package/dist/_esm/oauth.js +71 -53
- package/dist/_esm/provider.js +5 -2
- package/dist/_esm/store.js +4 -10
- package/dist/_esm/utils/aaUtils.js +2 -2
- package/dist/_types/actions.d.ts +35 -6
- package/dist/_types/actions.d.ts.map +1 -1
- package/dist/_types/connector.d.ts +1 -3
- package/dist/_types/connector.d.ts.map +1 -1
- package/dist/_types/constants.d.ts +1 -1
- package/dist/_types/constants.d.ts.map +1 -1
- package/dist/_types/hooks/useExportPrivateKey.d.ts +18 -0
- package/dist/_types/hooks/useExportPrivateKey.d.ts.map +1 -0
- package/dist/_types/hooks/useGetUserEmail.d.ts +18 -0
- package/dist/_types/hooks/useGetUserEmail.d.ts.map +1 -0
- package/dist/_types/index.d.ts +4 -2
- package/dist/_types/index.d.ts.map +1 -1
- package/dist/_types/oauth.d.ts +25 -12
- package/dist/_types/oauth.d.ts.map +1 -1
- package/dist/_types/provider.d.ts.map +1 -1
- package/dist/_types/store.d.ts +11 -7
- package/dist/_types/store.d.ts.map +1 -1
- package/dist/_types/utils/aaUtils.d.ts +1 -1
- package/dist/_types/utils/aaUtils.d.ts.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/actions.ts +124 -44
- package/src/connector.ts +130 -70
- package/src/constants.ts +2 -1
- package/src/hooks/useExportPrivateKey.ts +57 -0
- package/src/hooks/useGetUserEmail.ts +52 -0
- package/src/index.ts +9 -2
- package/src/oauth.ts +97 -78
- package/src/provider.ts +5 -2
- package/src/store.ts +15 -16
- package/src/utils/aaUtils.ts +2 -2
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zerodev/wallet-react",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.10",
|
|
4
4
|
"description": "React hooks for ZeroDev Wallet SDK",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/_cjs/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"wagmi": "^3.0.0",
|
|
31
31
|
"zustand": "^5.0.3",
|
|
32
32
|
"ox": "^0.3.0",
|
|
33
|
-
"@zerodev/wallet-core": "0.0.1-alpha.
|
|
33
|
+
"@zerodev/wallet-core": "0.0.1-alpha.9"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/react": "^19",
|
package/src/actions.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { Config, Connector } from '@wagmi/core'
|
|
2
2
|
import { connect as wagmiConnect } from '@wagmi/core/actions'
|
|
3
|
+
import {
|
|
4
|
+
createIframeStamper,
|
|
5
|
+
exportPrivateKey as exportPrivateKeySdk,
|
|
6
|
+
exportWallet as exportWalletSdk,
|
|
7
|
+
} from '@zerodev/wallet-core'
|
|
3
8
|
import type { OAuthProvider } from './oauth.js'
|
|
4
9
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
10
|
+
buildBackendOAuthUrl,
|
|
11
|
+
listenForOAuthMessage,
|
|
7
12
|
openOAuthPopup,
|
|
8
|
-
pollOAuthPopup,
|
|
9
13
|
} from './oauth.js'
|
|
10
14
|
|
|
11
15
|
/**
|
|
@@ -111,12 +115,12 @@ export declare namespace loginPasskey {
|
|
|
111
115
|
|
|
112
116
|
/**
|
|
113
117
|
* Authenticate with OAuth (opens popup)
|
|
118
|
+
* Uses backend OAuth flow where the backend handles PKCE and token exchange
|
|
114
119
|
*/
|
|
115
120
|
export async function authenticateOAuth(
|
|
116
121
|
config: Config,
|
|
117
122
|
parameters: {
|
|
118
123
|
provider: OAuthProvider
|
|
119
|
-
clientId?: string
|
|
120
124
|
connector?: Connector
|
|
121
125
|
},
|
|
122
126
|
): Promise<void> {
|
|
@@ -129,38 +133,23 @@ export async function authenticateOAuth(
|
|
|
129
133
|
|
|
130
134
|
if (!wallet) throw new Error('Wallet not initialized')
|
|
131
135
|
if (!oauthConfig) {
|
|
132
|
-
throw new Error(
|
|
133
|
-
'OAuth is not configured. Please provide oauthConfig to zeroDevWallet connector.',
|
|
134
|
-
)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Get client ID for the provider
|
|
138
|
-
let clientId = parameters.clientId
|
|
139
|
-
if (!clientId) {
|
|
140
|
-
clientId = oauthConfig.googleClientId
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (!clientId) {
|
|
144
|
-
throw new Error(`Client ID not configured for ${parameters.provider}`)
|
|
136
|
+
throw new Error('Wallet not initialized. Please wait for connector setup.')
|
|
145
137
|
}
|
|
146
138
|
|
|
147
|
-
|
|
148
|
-
throw new Error('OAuth redirect URI is not configured.')
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Generate nonce from wallet public key
|
|
139
|
+
// Get wallet public key for the OAuth flow
|
|
152
140
|
const publicKey = await wallet.getPublicKey()
|
|
153
141
|
if (!publicKey) {
|
|
154
142
|
throw new Error('Failed to get wallet public key')
|
|
155
143
|
}
|
|
156
|
-
const nonce = generateOAuthNonce(publicKey)
|
|
157
144
|
|
|
158
|
-
// Build OAuth URL
|
|
159
|
-
|
|
145
|
+
// Build OAuth URL that redirects to backend
|
|
146
|
+
// Use current origin as redirect - SDK auto-detects callback on any page
|
|
147
|
+
const oauthUrl = buildBackendOAuthUrl({
|
|
160
148
|
provider: parameters.provider,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
149
|
+
backendUrl: oauthConfig.backendUrl,
|
|
150
|
+
projectId: oauthConfig.projectId,
|
|
151
|
+
publicKey,
|
|
152
|
+
returnTo: `${window.location.origin}?oauth_success=true&oauth_provider=${parameters.provider}`,
|
|
164
153
|
})
|
|
165
154
|
|
|
166
155
|
// Open popup
|
|
@@ -170,18 +159,18 @@ export async function authenticateOAuth(
|
|
|
170
159
|
throw new Error(`Failed to open ${parameters.provider} login window.`)
|
|
171
160
|
}
|
|
172
161
|
|
|
173
|
-
//
|
|
162
|
+
// Listen for OAuth completion via postMessage
|
|
174
163
|
return new Promise<void>((resolve, reject) => {
|
|
175
|
-
|
|
164
|
+
const cleanup = listenForOAuthMessage(
|
|
176
165
|
authWindow,
|
|
177
166
|
window.location.origin,
|
|
178
|
-
async (
|
|
167
|
+
async () => {
|
|
179
168
|
try {
|
|
180
169
|
// Complete OAuth authentication with wallet-core
|
|
170
|
+
// The backend has stored the OAuth session in a cookie
|
|
181
171
|
await wallet.auth({
|
|
182
172
|
type: 'oauth',
|
|
183
173
|
provider: parameters.provider,
|
|
184
|
-
credential: idToken,
|
|
185
174
|
})
|
|
186
175
|
|
|
187
176
|
const [session, eoaAccount] = await Promise.all([
|
|
@@ -200,7 +189,10 @@ export async function authenticateOAuth(
|
|
|
200
189
|
reject(err)
|
|
201
190
|
}
|
|
202
191
|
},
|
|
203
|
-
|
|
192
|
+
(error) => {
|
|
193
|
+
cleanup()
|
|
194
|
+
reject(error)
|
|
195
|
+
},
|
|
204
196
|
)
|
|
205
197
|
})
|
|
206
198
|
}
|
|
@@ -208,7 +200,6 @@ export async function authenticateOAuth(
|
|
|
208
200
|
export declare namespace authenticateOAuth {
|
|
209
201
|
type Parameters = {
|
|
210
202
|
provider: OAuthProvider
|
|
211
|
-
clientId?: string
|
|
212
203
|
connector?: Connector
|
|
213
204
|
}
|
|
214
205
|
type ReturnType = void
|
|
@@ -225,7 +216,7 @@ export async function sendOTP(
|
|
|
225
216
|
emailCustomization?: { magicLinkTemplate?: string }
|
|
226
217
|
connector?: Connector
|
|
227
218
|
},
|
|
228
|
-
): Promise<{ otpId: string
|
|
219
|
+
): Promise<{ otpId: string }> {
|
|
229
220
|
const connector = parameters.connector ?? getZeroDevConnector(config)
|
|
230
221
|
|
|
231
222
|
// @ts-expect-error - getStore is a custom method
|
|
@@ -246,7 +237,6 @@ export async function sendOTP(
|
|
|
246
237
|
|
|
247
238
|
return {
|
|
248
239
|
otpId: result.otpId,
|
|
249
|
-
subOrganizationId: result.subOrganizationId,
|
|
250
240
|
}
|
|
251
241
|
}
|
|
252
242
|
|
|
@@ -256,7 +246,7 @@ export declare namespace sendOTP {
|
|
|
256
246
|
emailCustomization?: { magicLinkTemplate?: string }
|
|
257
247
|
connector?: Connector
|
|
258
248
|
}
|
|
259
|
-
type ReturnType = { otpId: string
|
|
249
|
+
type ReturnType = { otpId: string }
|
|
260
250
|
type ErrorType = Error
|
|
261
251
|
}
|
|
262
252
|
|
|
@@ -268,7 +258,6 @@ export async function verifyOTP(
|
|
|
268
258
|
parameters: {
|
|
269
259
|
code: string
|
|
270
260
|
otpId: string
|
|
271
|
-
subOrganizationId: string
|
|
272
261
|
connector?: Connector
|
|
273
262
|
},
|
|
274
263
|
): Promise<void> {
|
|
@@ -285,7 +274,6 @@ export async function verifyOTP(
|
|
|
285
274
|
mode: 'verifyOtp',
|
|
286
275
|
otpId: parameters.otpId,
|
|
287
276
|
otpCode: parameters.code,
|
|
288
|
-
subOrganizationId: parameters.subOrganizationId,
|
|
289
277
|
})
|
|
290
278
|
|
|
291
279
|
const [session, eoaAccount] = await Promise.all([
|
|
@@ -304,7 +292,6 @@ export declare namespace verifyOTP {
|
|
|
304
292
|
type Parameters = {
|
|
305
293
|
code: string
|
|
306
294
|
otpId: string
|
|
307
|
-
subOrganizationId: string
|
|
308
295
|
connector?: Connector
|
|
309
296
|
}
|
|
310
297
|
type ReturnType = void
|
|
@@ -344,6 +331,44 @@ export declare namespace refreshSession {
|
|
|
344
331
|
type ErrorType = Error
|
|
345
332
|
}
|
|
346
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Get user email
|
|
336
|
+
*/
|
|
337
|
+
export async function getUserEmail(config: Config): Promise<{ email: string }> {
|
|
338
|
+
const connector = getZeroDevConnector(config)
|
|
339
|
+
|
|
340
|
+
// @ts-expect-error - getStore is a custom method
|
|
341
|
+
const store = await connector.getStore()
|
|
342
|
+
const wallet = store.getState().wallet
|
|
343
|
+
|
|
344
|
+
if (!wallet) throw new Error('Wallet not initialized')
|
|
345
|
+
|
|
346
|
+
const oauthConfig = store.getState().oauthConfig
|
|
347
|
+
if (!oauthConfig) {
|
|
348
|
+
throw new Error('Wallet not initialized. Please wait for connector setup.')
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const session = store.getState().session
|
|
352
|
+
if (!session) {
|
|
353
|
+
throw new Error('No active session')
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Call the core SDK method
|
|
357
|
+
return await wallet.client.getUserEmail({
|
|
358
|
+
organizationId: session.organizationId,
|
|
359
|
+
projectId: oauthConfig.projectId,
|
|
360
|
+
token: session.token,
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export declare namespace getUserEmail {
|
|
365
|
+
type Parameters = {
|
|
366
|
+
connector?: Connector
|
|
367
|
+
}
|
|
368
|
+
type ReturnType = { email: string }
|
|
369
|
+
type ErrorType = Error
|
|
370
|
+
}
|
|
371
|
+
|
|
347
372
|
/**
|
|
348
373
|
* Export wallet
|
|
349
374
|
*/
|
|
@@ -362,10 +387,6 @@ export async function exportWallet(
|
|
|
362
387
|
|
|
363
388
|
if (!wallet) throw new Error('Wallet not initialized')
|
|
364
389
|
|
|
365
|
-
const { exportWallet: exportWalletSdk, createIframeStamper } = await import(
|
|
366
|
-
'@zerodev/wallet-core'
|
|
367
|
-
)
|
|
368
|
-
|
|
369
390
|
const iframeContainer = document.getElementById(parameters.iframeContainerId)
|
|
370
391
|
if (!iframeContainer) {
|
|
371
392
|
throw new Error('Iframe container not found')
|
|
@@ -400,3 +421,62 @@ export declare namespace exportWallet {
|
|
|
400
421
|
type ReturnType = void
|
|
401
422
|
type ErrorType = Error
|
|
402
423
|
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Export private key
|
|
427
|
+
*/
|
|
428
|
+
export async function exportPrivateKey(
|
|
429
|
+
config: Config,
|
|
430
|
+
parameters: {
|
|
431
|
+
iframeContainerId: string
|
|
432
|
+
address?: string
|
|
433
|
+
keyFormat?: 'Hexadecimal' | 'Solana'
|
|
434
|
+
connector?: Connector
|
|
435
|
+
},
|
|
436
|
+
): Promise<void> {
|
|
437
|
+
const connector = parameters.connector ?? getZeroDevConnector(config)
|
|
438
|
+
|
|
439
|
+
// @ts-expect-error - getStore is a custom method
|
|
440
|
+
const store = await connector.getStore()
|
|
441
|
+
const wallet = store.getState().wallet
|
|
442
|
+
|
|
443
|
+
if (!wallet) throw new Error('Wallet not initialized')
|
|
444
|
+
|
|
445
|
+
const iframeContainer = document.getElementById(parameters.iframeContainerId)
|
|
446
|
+
if (!iframeContainer) {
|
|
447
|
+
throw new Error('Iframe container not found')
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const iframeStamper = await createIframeStamper({
|
|
451
|
+
iframeUrl: 'https://export.turnkey.com',
|
|
452
|
+
iframeContainer,
|
|
453
|
+
iframeElementId: 'export-private-key-iframe',
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
const publicKey = await iframeStamper.init()
|
|
457
|
+
const { exportBundle, organizationId } = await exportPrivateKeySdk({
|
|
458
|
+
wallet,
|
|
459
|
+
targetPublicKey: publicKey,
|
|
460
|
+
...(parameters.address && { address: parameters.address }),
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
const success = await iframeStamper.injectKeyExportBundle(
|
|
464
|
+
exportBundle,
|
|
465
|
+
organizationId,
|
|
466
|
+
parameters.keyFormat ?? 'Hexadecimal',
|
|
467
|
+
)
|
|
468
|
+
if (success !== true) {
|
|
469
|
+
throw new Error('Failed to inject export bundle')
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export declare namespace exportPrivateKey {
|
|
474
|
+
type Parameters = {
|
|
475
|
+
iframeContainerId: string
|
|
476
|
+
address?: string
|
|
477
|
+
keyFormat?: 'Hexadecimal' | 'Solana'
|
|
478
|
+
connector?: Connector
|
|
479
|
+
}
|
|
480
|
+
type ReturnType = void
|
|
481
|
+
type ErrorType = Error
|
|
482
|
+
}
|
package/src/connector.ts
CHANGED
|
@@ -6,24 +6,81 @@ import {
|
|
|
6
6
|
} from '@zerodev/sdk'
|
|
7
7
|
import { getEntryPoint, KERNEL_V3_3 } from '@zerodev/sdk/constants'
|
|
8
8
|
import type { StorageAdapter } from '@zerodev/wallet-core'
|
|
9
|
-
import { createZeroDevWallet } from '@zerodev/wallet-core'
|
|
9
|
+
import { createZeroDevWallet, KMS_SERVER_URL } from '@zerodev/wallet-core'
|
|
10
10
|
import { type Chain, createPublicClient, http } from 'viem'
|
|
11
|
-
import type
|
|
11
|
+
import { handleOAuthCallback, type OAuthProvider } from './oauth.js'
|
|
12
12
|
import { createProvider } from './provider.js'
|
|
13
13
|
import { createZeroDevWalletStore } from './store.js'
|
|
14
14
|
import { getAAUrl } from './utils/aaUtils.js'
|
|
15
15
|
|
|
16
|
+
// OAuth URL parameter used to detect callback
|
|
17
|
+
const OAUTH_SUCCESS_PARAM = 'oauth_success'
|
|
18
|
+
const OAUTH_PROVIDER_PARAM = 'oauth_provider'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Detect OAuth callback from URL params and handle it.
|
|
22
|
+
* - If in popup: sends postMessage to opener and closes
|
|
23
|
+
* - If not in popup: completes auth directly
|
|
24
|
+
*/
|
|
25
|
+
async function detectAndHandleOAuthCallback(
|
|
26
|
+
wallet: Awaited<ReturnType<typeof createZeroDevWallet>>,
|
|
27
|
+
store: ReturnType<typeof createZeroDevWalletStore>,
|
|
28
|
+
): Promise<boolean> {
|
|
29
|
+
if (typeof window === 'undefined') return false
|
|
30
|
+
|
|
31
|
+
const params = new URLSearchParams(window.location.search)
|
|
32
|
+
const isOAuthCallback = params.get(OAUTH_SUCCESS_PARAM) === 'true'
|
|
33
|
+
|
|
34
|
+
if (!isOAuthCallback) return false
|
|
35
|
+
|
|
36
|
+
// If in popup, use the existing handler to notify opener
|
|
37
|
+
if (window.opener) {
|
|
38
|
+
handleOAuthCallback(OAUTH_SUCCESS_PARAM)
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Not in popup - complete auth directly (redirect flow)
|
|
43
|
+
console.log('OAuth callback detected, completing authentication...')
|
|
44
|
+
const provider = (params.get(OAUTH_PROVIDER_PARAM) ||
|
|
45
|
+
'google') as OAuthProvider
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await wallet.auth({ type: 'oauth', provider })
|
|
49
|
+
|
|
50
|
+
const [session, eoaAccount] = await Promise.all([
|
|
51
|
+
wallet.getSession(),
|
|
52
|
+
wallet.toAccount(),
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
store.getState().setEoaAccount(eoaAccount)
|
|
56
|
+
store.getState().setSession(session || null)
|
|
57
|
+
|
|
58
|
+
// Clean up URL params
|
|
59
|
+
params.delete(OAUTH_SUCCESS_PARAM)
|
|
60
|
+
params.delete(OAUTH_PROVIDER_PARAM)
|
|
61
|
+
const newUrl = params.toString()
|
|
62
|
+
? `${window.location.pathname}?${params.toString()}`
|
|
63
|
+
: window.location.pathname
|
|
64
|
+
window.history.replaceState({}, '', newUrl)
|
|
65
|
+
|
|
66
|
+
console.log('OAuth authentication completed')
|
|
67
|
+
return true
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('OAuth authentication failed:', error)
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
16
74
|
export type ZeroDevWalletConnectorParams = {
|
|
17
75
|
projectId: string
|
|
18
76
|
organizationId?: string
|
|
19
77
|
proxyBaseUrl?: string
|
|
20
|
-
aaUrl
|
|
78
|
+
aaUrl?: string // Bundler/paymaster URL
|
|
21
79
|
chains: readonly Chain[]
|
|
22
80
|
rpId?: string
|
|
23
81
|
sessionStorage?: StorageAdapter
|
|
24
82
|
autoRefreshSession?: boolean
|
|
25
83
|
sessionWarningThreshold?: number
|
|
26
|
-
oauthConfig?: OAuthConfig
|
|
27
84
|
}
|
|
28
85
|
|
|
29
86
|
export function zeroDevWallet(
|
|
@@ -50,62 +107,57 @@ export function zeroDevWallet(
|
|
|
50
107
|
return createConnector<Provider, Properties>((wagmiConfig) => {
|
|
51
108
|
let store: ReturnType<typeof createZeroDevWalletStore>
|
|
52
109
|
let provider: ReturnType<typeof createProvider>
|
|
53
|
-
let initPromise: Promise<void> | undefined
|
|
54
110
|
|
|
55
111
|
// Get transports from Wagmi config (uses user's RPC URLs)
|
|
56
112
|
const transports = wagmiConfig.transports
|
|
57
113
|
|
|
58
114
|
// Lazy initialization - only runs on client side
|
|
59
115
|
const initialize = async () => {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
console.log('ZeroDevWallet connector initialized')
|
|
106
|
-
})()
|
|
107
|
-
|
|
108
|
-
return initPromise
|
|
116
|
+
console.log('Initializing ZeroDevWallet connector...')
|
|
117
|
+
|
|
118
|
+
// Initialize wallet SDK
|
|
119
|
+
const wallet = await createZeroDevWallet({
|
|
120
|
+
projectId: params.projectId,
|
|
121
|
+
...(params.organizationId && {
|
|
122
|
+
organizationId: params.organizationId,
|
|
123
|
+
}),
|
|
124
|
+
...(params.proxyBaseUrl && { proxyBaseUrl: params.proxyBaseUrl }),
|
|
125
|
+
...(params.sessionStorage && {
|
|
126
|
+
sessionStorage: params.sessionStorage,
|
|
127
|
+
}),
|
|
128
|
+
...(params.rpId && { rpId: params.rpId }),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
// Create store
|
|
132
|
+
store = createZeroDevWalletStore()
|
|
133
|
+
store.getState().setWallet(wallet)
|
|
134
|
+
|
|
135
|
+
// Store OAuth config - uses proxyBaseUrl and projectId from params
|
|
136
|
+
store.getState().setOAuthConfig({
|
|
137
|
+
backendUrl: params.proxyBaseUrl || `${KMS_SERVER_URL}/api/v1`,
|
|
138
|
+
projectId: params.projectId,
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
// Create EIP-1193 provider
|
|
142
|
+
provider = createProvider({
|
|
143
|
+
store,
|
|
144
|
+
config: params,
|
|
145
|
+
chains: Array.from(params.chains),
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
// Check for existing session (page reload)
|
|
149
|
+
const session = await wallet.getSession()
|
|
150
|
+
if (session) {
|
|
151
|
+
console.log('Found existing session, restoring...')
|
|
152
|
+
const eoaAccount = await wallet.toAccount()
|
|
153
|
+
store.getState().setEoaAccount(eoaAccount)
|
|
154
|
+
store.getState().setSession(session)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Auto-detect OAuth callback (when popup redirects back with ?oauth_success=true)
|
|
158
|
+
await detectAndHandleOAuthCallback(wallet, store)
|
|
159
|
+
|
|
160
|
+
console.log('ZeroDevWallet connector initialized')
|
|
109
161
|
}
|
|
110
162
|
|
|
111
163
|
return {
|
|
@@ -137,10 +189,8 @@ export function zeroDevWallet(
|
|
|
137
189
|
const state = store.getState()
|
|
138
190
|
|
|
139
191
|
// Determine active chain
|
|
140
|
-
const activeChainId =
|
|
141
|
-
|
|
142
|
-
throw new Error('No chain configured')
|
|
143
|
-
}
|
|
192
|
+
const activeChainId =
|
|
193
|
+
chainId ?? state.activeChainId ?? params.chains[0].id
|
|
144
194
|
|
|
145
195
|
// If reconnecting and already have kernel account, return immediately
|
|
146
196
|
if (isReconnecting && state.kernelAccounts.has(activeChainId)) {
|
|
@@ -187,19 +237,23 @@ export function zeroDevWallet(
|
|
|
187
237
|
// Create and store kernel client for transactions
|
|
188
238
|
const kernelClient = createKernelAccountClient({
|
|
189
239
|
account: kernelAccount,
|
|
190
|
-
bundlerTransport: http(
|
|
240
|
+
bundlerTransport: http(
|
|
241
|
+
getAAUrl(params.projectId, activeChainId, params.aaUrl),
|
|
242
|
+
),
|
|
191
243
|
chain,
|
|
192
244
|
client: publicClient,
|
|
193
245
|
paymaster: createZeroDevPaymasterClient({
|
|
194
246
|
chain,
|
|
195
|
-
transport: http(
|
|
247
|
+
transport: http(
|
|
248
|
+
getAAUrl(params.projectId, activeChainId, params.aaUrl),
|
|
249
|
+
),
|
|
196
250
|
}),
|
|
197
251
|
})
|
|
198
252
|
store.getState().setKernelClient(activeChainId, kernelClient)
|
|
199
253
|
}
|
|
200
254
|
|
|
201
255
|
// Set as active chain
|
|
202
|
-
store.getState().
|
|
256
|
+
store.getState().setActiveChainId(activeChainId)
|
|
203
257
|
|
|
204
258
|
// Get fresh state after updates
|
|
205
259
|
const freshState = store.getState()
|
|
@@ -230,7 +284,7 @@ export function zeroDevWallet(
|
|
|
230
284
|
|
|
231
285
|
async getAccounts() {
|
|
232
286
|
if (!store) return []
|
|
233
|
-
const { eoaAccount, kernelAccounts,
|
|
287
|
+
const { eoaAccount, kernelAccounts, activeChainId } = store.getState()
|
|
234
288
|
|
|
235
289
|
// Return EOA address if we have it (EIP-7702: EOA address = kernel address)
|
|
236
290
|
if (eoaAccount) {
|
|
@@ -238,24 +292,25 @@ export function zeroDevWallet(
|
|
|
238
292
|
}
|
|
239
293
|
|
|
240
294
|
// Fallback: check kernel accounts
|
|
241
|
-
const activeAccount =
|
|
242
|
-
? kernelAccounts.get(
|
|
295
|
+
const activeAccount = activeChainId
|
|
296
|
+
? kernelAccounts.get(activeChainId)
|
|
243
297
|
: null
|
|
244
298
|
return activeAccount ? [activeAccount.address] : []
|
|
245
299
|
},
|
|
246
300
|
|
|
247
301
|
async getChainId() {
|
|
248
302
|
if (!store) return params.chains[0].id
|
|
249
|
-
return store.getState().
|
|
303
|
+
return store.getState().activeChainId ?? params.chains[0].id
|
|
250
304
|
},
|
|
251
305
|
|
|
252
306
|
async getProvider() {
|
|
253
|
-
|
|
307
|
+
if (!provider) {
|
|
308
|
+
await initialize()
|
|
309
|
+
}
|
|
254
310
|
return provider
|
|
255
311
|
},
|
|
256
312
|
|
|
257
313
|
async switchChain({ chainId }) {
|
|
258
|
-
await initialize()
|
|
259
314
|
console.log(`Switching to chain ${chainId}...`)
|
|
260
315
|
const state = store.getState()
|
|
261
316
|
|
|
@@ -264,7 +319,7 @@ export function zeroDevWallet(
|
|
|
264
319
|
}
|
|
265
320
|
|
|
266
321
|
// Update active chain
|
|
267
|
-
store.getState().
|
|
322
|
+
store.getState().setActiveChainId(chainId)
|
|
268
323
|
|
|
269
324
|
// Create kernel account for new chain if doesn't exist
|
|
270
325
|
if (!state.kernelAccounts.has(chainId)) {
|
|
@@ -290,17 +345,22 @@ export function zeroDevWallet(
|
|
|
290
345
|
store.getState().setKernelAccount(chainId, kernelAccount)
|
|
291
346
|
const kernelClient = createKernelAccountClient({
|
|
292
347
|
account: kernelAccount,
|
|
293
|
-
bundlerTransport: http(
|
|
348
|
+
bundlerTransport: http(
|
|
349
|
+
getAAUrl(params.projectId, chainId, params.aaUrl),
|
|
350
|
+
),
|
|
294
351
|
chain,
|
|
295
352
|
client: publicClient,
|
|
296
353
|
paymaster: createZeroDevPaymasterClient({
|
|
297
354
|
chain,
|
|
298
|
-
transport: http(
|
|
355
|
+
transport: http(
|
|
356
|
+
getAAUrl(params.projectId, chainId, params.aaUrl),
|
|
357
|
+
),
|
|
299
358
|
}),
|
|
300
359
|
})
|
|
301
360
|
store.getState().setKernelClient(chainId, kernelClient)
|
|
302
361
|
}
|
|
303
362
|
|
|
363
|
+
wagmiConfig.emitter.emit('change', { chainId })
|
|
304
364
|
return params.chains.find((c) => c.id === chainId)!
|
|
305
365
|
},
|
|
306
366
|
|
package/src/constants.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const ZERODEV_AA_URL =
|
|
1
|
+
export const ZERODEV_AA_URL =
|
|
2
|
+
'https://staging-meta-aa-provider.onrender.com/api/v3/'
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type UseMutationOptions,
|
|
5
|
+
type UseMutationResult,
|
|
6
|
+
useMutation,
|
|
7
|
+
} from '@tanstack/react-query'
|
|
8
|
+
import { type Config, type ResolvedRegister, useConfig } from 'wagmi'
|
|
9
|
+
import { exportPrivateKey } from '../actions.js'
|
|
10
|
+
|
|
11
|
+
type ConfigParameter<config extends Config = Config> = {
|
|
12
|
+
config?: Config | config | undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Hook to export private key
|
|
17
|
+
*/
|
|
18
|
+
export function useExportPrivateKey<
|
|
19
|
+
config extends Config = ResolvedRegister['config'],
|
|
20
|
+
context = unknown,
|
|
21
|
+
>(
|
|
22
|
+
parameters: useExportPrivateKey.Parameters<config, context> = {},
|
|
23
|
+
): useExportPrivateKey.ReturnType<context> {
|
|
24
|
+
const { mutation } = parameters
|
|
25
|
+
const config = useConfig(parameters)
|
|
26
|
+
|
|
27
|
+
return useMutation({
|
|
28
|
+
...mutation,
|
|
29
|
+
async mutationFn(variables: exportPrivateKey.Parameters) {
|
|
30
|
+
return exportPrivateKey(config, variables)
|
|
31
|
+
},
|
|
32
|
+
mutationKey: ['exportPrivateKey'],
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export declare namespace useExportPrivateKey {
|
|
37
|
+
type Parameters<
|
|
38
|
+
config extends Config = Config,
|
|
39
|
+
context = unknown,
|
|
40
|
+
> = ConfigParameter<config> & {
|
|
41
|
+
mutation?:
|
|
42
|
+
| UseMutationOptions<
|
|
43
|
+
exportPrivateKey.ReturnType,
|
|
44
|
+
exportPrivateKey.ErrorType,
|
|
45
|
+
exportPrivateKey.Parameters,
|
|
46
|
+
context
|
|
47
|
+
>
|
|
48
|
+
| undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type ReturnType<context = unknown> = UseMutationResult<
|
|
52
|
+
exportPrivateKey.ReturnType,
|
|
53
|
+
exportPrivateKey.ErrorType,
|
|
54
|
+
exportPrivateKey.Parameters,
|
|
55
|
+
context
|
|
56
|
+
>
|
|
57
|
+
}
|