@zerodev/wallet-react 0.0.1-alpha.12 → 0.0.1-alpha.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerodev/wallet-react",
3
- "version": "0.0.1-alpha.12",
3
+ "version": "0.0.1-alpha.14",
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.11"
33
+ "@zerodev/wallet-core": "0.0.1-alpha.13"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/react": "^19",
@@ -108,11 +108,10 @@ describe('React Actions', () => {
108
108
  const connector = createMockConnector(store)
109
109
  const config = createMockConfig(connector)
110
110
 
111
- await registerPasskey(config, { email: 'user@example.com' })
111
+ await registerPasskey(config, {})
112
112
 
113
113
  expect(wallet.auth).toHaveBeenCalledWith({
114
114
  type: 'passkey',
115
- email: 'user@example.com',
116
115
  mode: 'register',
117
116
  })
118
117
  })
@@ -125,7 +124,7 @@ describe('React Actions', () => {
125
124
  const connector = createMockConnector(store)
126
125
  const config = createMockConfig(connector)
127
126
 
128
- await registerPasskey(config, { email: 'user@example.com' })
127
+ await registerPasskey(config, {})
129
128
 
130
129
  expect(store.getState().setEoaAccount).toHaveBeenCalledWith({
131
130
  address: '0xdeadbeef',
@@ -143,7 +142,7 @@ describe('React Actions', () => {
143
142
  const connector = createMockConnector(store)
144
143
  const config = createMockConfig(connector)
145
144
 
146
- await registerPasskey(config, { email: 'user@example.com' })
145
+ await registerPasskey(config, {})
147
146
 
148
147
  expect(store.getState().setSession).toHaveBeenCalledWith(null)
149
148
  })
@@ -154,7 +153,7 @@ describe('React Actions', () => {
154
153
  const connector = createMockConnector(store)
155
154
  const config = createMockConfig(connector)
156
155
 
157
- await registerPasskey(config, { email: 'user@example.com' })
156
+ await registerPasskey(config, {})
158
157
 
159
158
  expect(wagmiConnect).toHaveBeenCalledWith(config, { connector })
160
159
  })
@@ -164,9 +163,9 @@ describe('React Actions', () => {
164
163
  const connector = createMockConnector(store)
165
164
  const config = createMockConfig(connector)
166
165
 
167
- await expect(
168
- registerPasskey(config, { email: 'user@example.com' }),
169
- ).rejects.toThrow('Wallet not initialized')
166
+ await expect(registerPasskey(config, {})).rejects.toThrow(
167
+ 'Wallet not initialized',
168
+ )
170
169
  })
171
170
 
172
171
  it('uses provided connector instead of finding one', async () => {
@@ -176,7 +175,6 @@ describe('React Actions', () => {
176
175
  const config = createMockConfig()
177
176
 
178
177
  await registerPasskey(config, {
179
- email: 'user@example.com',
180
178
  connector: customConnector,
181
179
  })
182
180
 
@@ -191,11 +189,10 @@ describe('React Actions', () => {
191
189
  const connector = createMockConnector(store)
192
190
  const config = createMockConfig(connector)
193
191
 
194
- await loginPasskey(config, { email: 'user@example.com' })
192
+ await loginPasskey(config, {})
195
193
 
196
194
  expect(wallet.auth).toHaveBeenCalledWith({
197
195
  type: 'passkey',
198
- email: 'user@example.com',
199
196
  mode: 'login',
200
197
  })
201
198
  })
@@ -208,7 +205,7 @@ describe('React Actions', () => {
208
205
  const connector = createMockConnector(store)
209
206
  const config = createMockConfig(connector)
210
207
 
211
- await loginPasskey(config, { email: 'user@example.com' })
208
+ await loginPasskey(config, {})
212
209
 
213
210
  expect(store.getState().setEoaAccount).toHaveBeenCalledWith({
214
211
  address: '0xabc',
@@ -224,7 +221,7 @@ describe('React Actions', () => {
224
221
  const connector = createMockConnector(store)
225
222
  const config = createMockConfig(connector)
226
223
 
227
- await loginPasskey(config, { email: 'user@example.com' })
224
+ await loginPasskey(config, {})
228
225
 
229
226
  expect(wagmiConnect).toHaveBeenCalledWith(config, { connector })
230
227
  })
@@ -234,9 +231,9 @@ describe('React Actions', () => {
234
231
  const connector = createMockConnector(store)
235
232
  const config = createMockConfig(connector)
236
233
 
237
- await expect(
238
- loginPasskey(config, { email: 'user@example.com' }),
239
- ).rejects.toThrow('Wallet not initialized')
234
+ await expect(loginPasskey(config, {})).rejects.toThrow(
235
+ 'Wallet not initialized',
236
+ )
240
237
  })
241
238
  })
242
239
 
@@ -887,9 +884,9 @@ describe('React Actions', () => {
887
884
  storage: null,
888
885
  } as unknown as Config
889
886
 
890
- await expect(
891
- registerPasskey(config, { email: 'user@example.com' }),
892
- ).rejects.toThrow('ZeroDev connector not found in Wagmi config')
887
+ await expect(registerPasskey(config, {})).rejects.toThrow(
888
+ 'ZeroDev connector not found in Wagmi config',
889
+ )
893
890
  })
894
891
  })
895
892
  })
package/src/actions.ts CHANGED
@@ -28,12 +28,11 @@ function getZeroDevConnector(config: Config): Connector {
28
28
  */
29
29
  export async function registerPasskey(
30
30
  config: Config,
31
- parameters: {
32
- email: string
31
+ parameters?: {
33
32
  connector?: Connector
34
33
  },
35
34
  ): Promise<void> {
36
- const connector = parameters.connector ?? getZeroDevConnector(config)
35
+ const connector = parameters?.connector ?? getZeroDevConnector(config)
37
36
 
38
37
  // @ts-expect-error - getStore is a custom method
39
38
  const store = await connector.getStore()
@@ -43,7 +42,6 @@ export async function registerPasskey(
43
42
 
44
43
  await wallet.auth({
45
44
  type: 'passkey',
46
- email: parameters.email,
47
45
  mode: 'register',
48
46
  })
49
47
 
@@ -60,8 +58,7 @@ export async function registerPasskey(
60
58
  }
61
59
 
62
60
  export declare namespace registerPasskey {
63
- type Parameters = {
64
- email: string
61
+ type Parameters = void | {
65
62
  connector?: Connector
66
63
  }
67
64
  type ReturnType = void
@@ -73,12 +70,11 @@ export declare namespace registerPasskey {
73
70
  */
74
71
  export async function loginPasskey(
75
72
  config: Config,
76
- parameters: {
77
- email: string
73
+ parameters?: {
78
74
  connector?: Connector
79
75
  },
80
76
  ): Promise<void> {
81
- const connector = parameters.connector ?? getZeroDevConnector(config)
77
+ const connector = parameters?.connector ?? getZeroDevConnector(config)
82
78
 
83
79
  // @ts-expect-error - getStore is a custom method
84
80
  const store = await connector.getStore()
@@ -88,7 +84,6 @@ export async function loginPasskey(
88
84
 
89
85
  await wallet.auth({
90
86
  type: 'passkey',
91
- email: parameters.email,
92
87
  mode: 'login',
93
88
  })
94
89
 
@@ -105,8 +100,7 @@ export async function loginPasskey(
105
100
  }
106
101
 
107
102
  export declare namespace loginPasskey {
108
- type Parameters = {
109
- email: string
103
+ type Parameters = void | {
110
104
  connector?: Connector
111
105
  }
112
106
  type ReturnType = void
@@ -214,6 +208,7 @@ export async function sendOTP(
214
208
  parameters: {
215
209
  email: string
216
210
  emailCustomization?: { magicLinkTemplate?: string }
211
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
217
212
  connector?: Connector
218
213
  },
219
214
  ): Promise<{ otpId: string }> {
@@ -233,6 +228,9 @@ export async function sendOTP(
233
228
  ...(parameters.emailCustomization && {
234
229
  emailCustomization: parameters.emailCustomization,
235
230
  }),
231
+ ...(parameters.otpCodeCustomization && {
232
+ otpCodeCustomization: parameters.otpCodeCustomization,
233
+ }),
236
234
  })
237
235
 
238
236
  return {
@@ -244,6 +242,7 @@ export declare namespace sendOTP {
244
242
  type Parameters = {
245
243
  email: string
246
244
  emailCustomization?: { magicLinkTemplate?: string }
245
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
247
246
  connector?: Connector
248
247
  }
249
248
  type ReturnType = { otpId: string }
@@ -376,6 +375,7 @@ export async function exportWallet(
376
375
  config: Config,
377
376
  parameters: {
378
377
  iframeContainerId: string
378
+ iframeStyles?: Record<string, string>
379
379
  connector?: Connector
380
380
  },
381
381
  ): Promise<void> {
@@ -399,6 +399,11 @@ export async function exportWallet(
399
399
  })
400
400
 
401
401
  const publicKey = await iframeStamper.init()
402
+
403
+ if (parameters.iframeStyles) {
404
+ await iframeStamper.applySettings({ styles: parameters.iframeStyles })
405
+ }
406
+
402
407
  const { exportBundle, organizationId } = await exportWalletSdk({
403
408
  wallet,
404
409
  targetPublicKey: publicKey,
@@ -416,6 +421,7 @@ export async function exportWallet(
416
421
  export declare namespace exportWallet {
417
422
  type Parameters = {
418
423
  iframeContainerId: string
424
+ iframeStyles?: Record<string, string>
419
425
  connector?: Connector
420
426
  }
421
427
  type ReturnType = void
@@ -429,6 +435,7 @@ export async function exportPrivateKey(
429
435
  config: Config,
430
436
  parameters: {
431
437
  iframeContainerId: string
438
+ iframeStyles?: Record<string, string>
432
439
  address?: string
433
440
  keyFormat?: 'Hexadecimal' | 'Solana'
434
441
  connector?: Connector
@@ -454,6 +461,11 @@ export async function exportPrivateKey(
454
461
  })
455
462
 
456
463
  const publicKey = await iframeStamper.init()
464
+
465
+ if (parameters.iframeStyles) {
466
+ await iframeStamper.applySettings({ styles: parameters.iframeStyles })
467
+ }
468
+
457
469
  const { exportBundle, organizationId } = await exportPrivateKeySdk({
458
470
  wallet,
459
471
  targetPublicKey: publicKey,
@@ -473,6 +485,7 @@ export async function exportPrivateKey(
473
485
  export declare namespace exportPrivateKey {
474
486
  type Parameters = {
475
487
  iframeContainerId: string
488
+ iframeStyles?: Record<string, string>
476
489
  address?: string
477
490
  keyFormat?: 'Hexadecimal' | 'Solana'
478
491
  connector?: Connector
@@ -480,3 +493,97 @@ export declare namespace exportPrivateKey {
480
493
  type ReturnType = void
481
494
  type ErrorType = Error
482
495
  }
496
+
497
+ /**
498
+ * Send magic link via email
499
+ */
500
+ export async function sendMagicLink(
501
+ config: Config,
502
+ parameters: {
503
+ email: string
504
+ redirectURL: string
505
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
506
+ connector?: Connector
507
+ },
508
+ ): Promise<{ otpId: string }> {
509
+ const connector = parameters.connector ?? getZeroDevConnector(config)
510
+
511
+ // @ts-expect-error - getStore is a custom method
512
+ const store = await connector.getStore()
513
+ const wallet = store.getState().wallet
514
+
515
+ if (!wallet) throw new Error('Wallet not initialized')
516
+
517
+ const result = await wallet.auth({
518
+ type: 'magicLink',
519
+ mode: 'send',
520
+ email: parameters.email,
521
+ redirectURL: parameters.redirectURL,
522
+ ...(parameters.otpCodeCustomization && {
523
+ otpCodeCustomization: parameters.otpCodeCustomization,
524
+ }),
525
+ })
526
+
527
+ return {
528
+ otpId: result.otpId,
529
+ }
530
+ }
531
+
532
+ export declare namespace sendMagicLink {
533
+ type Parameters = {
534
+ email: string
535
+ redirectURL: string
536
+ otpCodeCustomization?: { length: 6 | 7 | 8 | 9; alphanumeric: boolean }
537
+ connector?: Connector
538
+ }
539
+ type ReturnType = { otpId: string }
540
+ type ErrorType = Error
541
+ }
542
+
543
+ /**
544
+ * Verify magic link code
545
+ */
546
+ export async function verifyMagicLink(
547
+ config: Config,
548
+ parameters: {
549
+ otpId: string
550
+ code: string
551
+ connector?: Connector
552
+ },
553
+ ): Promise<void> {
554
+ const connector = parameters.connector ?? getZeroDevConnector(config)
555
+
556
+ // @ts-expect-error - getStore is a custom method
557
+ const store = await connector.getStore()
558
+ const wallet = store.getState().wallet
559
+
560
+ if (!wallet) throw new Error('Wallet not initialized')
561
+
562
+ await wallet.auth({
563
+ type: 'magicLink',
564
+ mode: 'verify',
565
+ otpId: parameters.otpId,
566
+ code: parameters.code,
567
+ })
568
+
569
+ const [session, eoaAccount] = await Promise.all([
570
+ wallet.getSession(),
571
+ wallet.toAccount(),
572
+ ])
573
+
574
+ store.getState().setEoaAccount(eoaAccount)
575
+ store.getState().setSession(session || null)
576
+
577
+ // Auto-connect to Wagmi
578
+ await wagmiConnect(config, { connector })
579
+ }
580
+
581
+ export declare namespace verifyMagicLink {
582
+ type Parameters = {
583
+ otpId: string
584
+ code: string
585
+ connector?: Connector
586
+ }
587
+ type ReturnType = void
588
+ type ErrorType = Error
589
+ }
@@ -26,8 +26,8 @@ export function useLoginPasskey<
26
26
 
27
27
  return useMutation({
28
28
  ...mutation,
29
- async mutationFn(variables: loginPasskey.Parameters) {
30
- return loginPasskey(config, variables)
29
+ async mutationFn(variables?: loginPasskey.Parameters) {
30
+ return loginPasskey(config, variables ?? undefined)
31
31
  },
32
32
  mutationKey: ['loginPasskey'],
33
33
  })
@@ -26,8 +26,8 @@ export function useRegisterPasskey<
26
26
 
27
27
  return useMutation({
28
28
  ...mutation,
29
- async mutationFn(variables: registerPasskey.Parameters) {
30
- return registerPasskey(config, variables)
29
+ async mutationFn(variables?: registerPasskey.Parameters) {
30
+ return registerPasskey(config, variables ?? undefined)
31
31
  },
32
32
  mutationKey: ['registerPasskey'],
33
33
  })
@@ -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 { sendMagicLink } from '../actions.js'
10
+
11
+ type ConfigParameter<config extends Config = Config> = {
12
+ config?: Config | config | undefined
13
+ }
14
+
15
+ /**
16
+ * Hook to send a magic link via email
17
+ */
18
+ export function useSendMagicLink<
19
+ config extends Config = ResolvedRegister['config'],
20
+ context = unknown,
21
+ >(
22
+ parameters: useSendMagicLink.Parameters<config, context> = {},
23
+ ): useSendMagicLink.ReturnType<context> {
24
+ const { mutation } = parameters
25
+ const config = useConfig(parameters)
26
+
27
+ return useMutation({
28
+ ...mutation,
29
+ async mutationFn(variables: sendMagicLink.Parameters) {
30
+ return sendMagicLink(config, variables)
31
+ },
32
+ mutationKey: ['sendMagicLink'],
33
+ })
34
+ }
35
+
36
+ export declare namespace useSendMagicLink {
37
+ type Parameters<
38
+ config extends Config = Config,
39
+ context = unknown,
40
+ > = ConfigParameter<config> & {
41
+ mutation?:
42
+ | UseMutationOptions<
43
+ sendMagicLink.ReturnType,
44
+ sendMagicLink.ErrorType,
45
+ sendMagicLink.Parameters,
46
+ context
47
+ >
48
+ | undefined
49
+ }
50
+
51
+ type ReturnType<context = unknown> = UseMutationResult<
52
+ sendMagicLink.ReturnType,
53
+ sendMagicLink.ErrorType,
54
+ sendMagicLink.Parameters,
55
+ context
56
+ >
57
+ }
@@ -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 { verifyMagicLink } from '../actions.js'
10
+
11
+ type ConfigParameter<config extends Config = Config> = {
12
+ config?: Config | config | undefined
13
+ }
14
+
15
+ /**
16
+ * Hook to verify a magic link code
17
+ */
18
+ export function useVerifyMagicLink<
19
+ config extends Config = ResolvedRegister['config'],
20
+ context = unknown,
21
+ >(
22
+ parameters: useVerifyMagicLink.Parameters<config, context> = {},
23
+ ): useVerifyMagicLink.ReturnType<context> {
24
+ const { mutation } = parameters
25
+ const config = useConfig(parameters)
26
+
27
+ return useMutation({
28
+ ...mutation,
29
+ async mutationFn(variables: verifyMagicLink.Parameters) {
30
+ return verifyMagicLink(config, variables)
31
+ },
32
+ mutationKey: ['verifyMagicLink'],
33
+ })
34
+ }
35
+
36
+ export declare namespace useVerifyMagicLink {
37
+ type Parameters<
38
+ config extends Config = Config,
39
+ context = unknown,
40
+ > = ConfigParameter<config> & {
41
+ mutation?:
42
+ | UseMutationOptions<
43
+ verifyMagicLink.ReturnType,
44
+ verifyMagicLink.ErrorType,
45
+ verifyMagicLink.Parameters,
46
+ context
47
+ >
48
+ | undefined
49
+ }
50
+
51
+ type ReturnType<context = unknown> = UseMutationResult<
52
+ verifyMagicLink.ReturnType,
53
+ verifyMagicLink.ErrorType,
54
+ verifyMagicLink.Parameters,
55
+ context
56
+ >
57
+ }
package/src/index.ts CHANGED
@@ -7,7 +7,9 @@ export { useGetUserEmail } from './hooks/useGetUserEmail.js'
7
7
  export { useLoginPasskey } from './hooks/useLoginPasskey.js'
8
8
  export { useRefreshSession } from './hooks/useRefreshSession.js'
9
9
  export { useRegisterPasskey } from './hooks/useRegisterPasskey.js'
10
+ export { useSendMagicLink } from './hooks/useSendMagicLink.js'
10
11
  export { useSendOTP } from './hooks/useSendOTP.js'
12
+ export { useVerifyMagicLink } from './hooks/useVerifyMagicLink.js'
11
13
  export { useVerifyOTP } from './hooks/useVerifyOTP.js'
12
14
  export type { OAuthMessageData, OAuthProvider } from './oauth.js'
13
15
  export {
package/src/provider.ts CHANGED
@@ -151,7 +151,8 @@ export function createProvider({
151
151
  throw new Error(`No kernel client for chain ${chainId}`)
152
152
  }
153
153
 
154
- // Send gasless transaction (always UserOp for EIP-7702)
154
+ // Transactions are sent as UserOperations under the hood (EIP-7702).
155
+ // Gasless if a paymaster is configured on the ZeroDev dashboard.
155
156
  const hash = await kernelClient.sendTransaction({
156
157
  calls: [
157
158
  {