@zerodev/wallet-core 0.0.1-alpha.19 → 0.0.1-alpha.21

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 (116) hide show
  1. package/README.md +1 -1
  2. package/dist/_cjs/actions/auth/authenticateWithOAuth.js +2 -2
  3. package/dist/_cjs/actions/auth/authenticateWithOAuth.js.map +1 -1
  4. package/dist/_cjs/client/index.js.map +1 -1
  5. package/dist/_cjs/client/transports/createTransport.js +1 -0
  6. package/dist/_cjs/client/transports/createTransport.js.map +1 -1
  7. package/dist/_cjs/client/transports/rest.js +2 -2
  8. package/dist/_cjs/client/transports/rest.js.map +1 -1
  9. package/dist/_cjs/core/{createZeroDevWallet.js → createZeroDevWalletCore.js} +8 -10
  10. package/dist/_cjs/core/createZeroDevWalletCore.js.map +1 -0
  11. package/dist/_cjs/index.js +9 -7
  12. package/dist/_cjs/index.js.map +1 -1
  13. package/dist/_cjs/index.native.js +39 -0
  14. package/dist/_cjs/index.native.js.map +1 -0
  15. package/dist/_cjs/native/stampers/passkey.js +30 -0
  16. package/dist/_cjs/native/stampers/passkey.js.map +1 -0
  17. package/dist/_cjs/native/stampers/secureStore.js +112 -0
  18. package/dist/_cjs/native/stampers/secureStore.js.map +1 -0
  19. package/dist/_cjs/native/storage/asyncStorage.js +11 -0
  20. package/dist/_cjs/native/storage/asyncStorage.js.map +1 -0
  21. package/dist/_cjs/stampers/indexedDbStamper.js +3 -0
  22. package/dist/_cjs/stampers/indexedDbStamper.js.map +1 -1
  23. package/dist/_cjs/stubs/native-on-web.js +3 -0
  24. package/dist/_cjs/stubs/native-on-web.js.map +1 -0
  25. package/dist/_cjs/utils/platform.js +19 -0
  26. package/dist/_cjs/utils/platform.js.map +1 -0
  27. package/dist/_cjs/web/createZeroDevWallet.js +21 -0
  28. package/dist/_cjs/web/createZeroDevWallet.js.map +1 -0
  29. package/dist/_esm/actions/auth/authenticateWithOAuth.js +3 -2
  30. package/dist/_esm/actions/auth/authenticateWithOAuth.js.map +1 -1
  31. package/dist/_esm/client/index.js +1 -1
  32. package/dist/_esm/client/index.js.map +1 -1
  33. package/dist/_esm/client/transports/createTransport.js +1 -0
  34. package/dist/_esm/client/transports/createTransport.js.map +1 -1
  35. package/dist/_esm/client/transports/rest.js +2 -2
  36. package/dist/_esm/client/transports/rest.js.map +1 -1
  37. package/dist/_esm/core/{createZeroDevWallet.js → createZeroDevWalletCore.js} +7 -9
  38. package/dist/_esm/core/createZeroDevWalletCore.js.map +1 -0
  39. package/dist/_esm/index.js +11 -3
  40. package/dist/_esm/index.js.map +1 -1
  41. package/dist/_esm/index.native.js +18 -0
  42. package/dist/_esm/index.native.js.map +1 -0
  43. package/dist/_esm/native/stampers/passkey.js +27 -0
  44. package/dist/_esm/native/stampers/passkey.js.map +1 -0
  45. package/dist/_esm/native/stampers/secureStore.js +120 -0
  46. package/dist/_esm/native/stampers/secureStore.js.map +1 -0
  47. package/dist/_esm/native/storage/asyncStorage.js +13 -0
  48. package/dist/_esm/native/storage/asyncStorage.js.map +1 -0
  49. package/dist/_esm/stampers/indexedDbStamper.js +3 -0
  50. package/dist/_esm/stampers/indexedDbStamper.js.map +1 -1
  51. package/dist/_esm/stubs/native-on-web.js +3 -0
  52. package/dist/_esm/stubs/native-on-web.js.map +1 -0
  53. package/dist/_esm/utils/platform.js +16 -0
  54. package/dist/_esm/utils/platform.js.map +1 -0
  55. package/dist/_esm/web/createZeroDevWallet.js +18 -0
  56. package/dist/_esm/web/createZeroDevWallet.js.map +1 -0
  57. package/dist/_types/actions/auth/authenticateWithOAuth.d.ts +3 -0
  58. package/dist/_types/actions/auth/authenticateWithOAuth.d.ts.map +1 -1
  59. package/dist/_types/client/index.d.ts +1 -1
  60. package/dist/_types/client/index.d.ts.map +1 -1
  61. package/dist/_types/client/transports/createTransport.d.ts +2 -0
  62. package/dist/_types/client/transports/createTransport.d.ts.map +1 -1
  63. package/dist/_types/core/{createZeroDevWallet.d.ts → createZeroDevWalletCore.d.ts} +9 -8
  64. package/dist/_types/core/createZeroDevWalletCore.d.ts.map +1 -0
  65. package/dist/_types/index.d.ts +4 -4
  66. package/dist/_types/index.d.ts.map +1 -1
  67. package/dist/_types/index.native.d.ts +19 -0
  68. package/dist/_types/index.native.d.ts.map +1 -0
  69. package/dist/_types/native/stampers/passkey.d.ts +5 -0
  70. package/dist/_types/native/stampers/passkey.d.ts.map +1 -0
  71. package/dist/_types/native/stampers/secureStore.d.ts +3 -0
  72. package/dist/_types/native/stampers/secureStore.d.ts.map +1 -0
  73. package/dist/_types/native/storage/asyncStorage.d.ts +3 -0
  74. package/dist/_types/native/storage/asyncStorage.d.ts.map +1 -0
  75. package/dist/_types/stampers/indexedDbStamper.d.ts.map +1 -1
  76. package/dist/_types/stampers/types.d.ts +5 -0
  77. package/dist/_types/stampers/types.d.ts.map +1 -1
  78. package/dist/_types/stubs/native-on-web.d.ts +2 -0
  79. package/dist/_types/stubs/native-on-web.d.ts.map +1 -0
  80. package/dist/_types/utils/exportPrivateKey.d.ts +1 -1
  81. package/dist/_types/utils/exportPrivateKey.d.ts.map +1 -1
  82. package/dist/_types/utils/exportWallet.d.ts +1 -1
  83. package/dist/_types/utils/exportWallet.d.ts.map +1 -1
  84. package/dist/_types/utils/platform.d.ts +2 -0
  85. package/dist/_types/utils/platform.d.ts.map +1 -0
  86. package/dist/_types/web/createZeroDevWallet.d.ts +18 -0
  87. package/dist/_types/web/createZeroDevWallet.d.ts.map +1 -0
  88. package/dist/tsconfig.build.tsbuildinfo +1 -1
  89. package/package.json +92 -11
  90. package/src/actions/auth/authenticateWithOAuth.ts +5 -2
  91. package/src/client/index.ts +4 -1
  92. package/src/client/transports/createTransport.ts +3 -0
  93. package/src/client/transports/rest.ts +2 -2
  94. package/src/core/{createZeroDevWallet.ts → createZeroDevWalletCore.ts} +16 -19
  95. package/src/index.native.ts +102 -0
  96. package/src/index.ts +25 -10
  97. package/src/native/stampers/passkey.ts +39 -0
  98. package/src/native/stampers/secureStore.ts +139 -0
  99. package/src/native/storage/asyncStorage.ts +18 -0
  100. package/src/stampers/indexedDbStamper.ts +3 -0
  101. package/src/stampers/types.ts +5 -0
  102. package/src/stubs/native-on-web.ts +3 -0
  103. package/src/utils/exportPrivateKey.ts +1 -1
  104. package/src/utils/exportWallet.ts +1 -1
  105. package/src/utils/platform.ts +21 -0
  106. package/src/web/createZeroDevWallet.ts +45 -0
  107. package/dist/_cjs/core/createZeroDevWallet.js.map +0 -1
  108. package/dist/_cjs/stampers/index.js +0 -10
  109. package/dist/_cjs/stampers/index.js.map +0 -1
  110. package/dist/_esm/core/createZeroDevWallet.js.map +0 -1
  111. package/dist/_esm/stampers/index.js +0 -4
  112. package/dist/_esm/stampers/index.js.map +0 -1
  113. package/dist/_types/core/createZeroDevWallet.d.ts.map +0 -1
  114. package/dist/_types/stampers/index.d.ts +0 -5
  115. package/dist/_types/stampers/index.d.ts.map +0 -1
  116. package/src/stampers/index.ts +0 -8
package/package.json CHANGED
@@ -1,15 +1,65 @@
1
1
  {
2
2
  "name": "@zerodev/wallet-core",
3
- "version": "0.0.1-alpha.19",
3
+ "version": "0.0.1-alpha.21",
4
4
  "description": "ZeroDev Wallet SDK built on Turnkey",
5
+ "sideEffects": false,
5
6
  "main": "./dist/_cjs/index.js",
6
7
  "module": "./dist/_esm/index.js",
7
8
  "types": "./dist/_types/index.d.ts",
8
9
  "exports": {
9
10
  ".": {
10
- "types": "./dist/_types/index.d.ts",
11
- "import": "./dist/_esm/index.js",
12
- "require": "./dist/_cjs/index.js"
11
+ "react-native": {
12
+ "types": "./dist/_types/index.native.d.ts",
13
+ "default": "./dist/_esm/index.native.js"
14
+ },
15
+ "browser": {
16
+ "types": "./dist/_types/index.d.ts",
17
+ "default": "./dist/_esm/index.js"
18
+ },
19
+ "import": {
20
+ "types": "./dist/_types/index.d.ts",
21
+ "default": "./dist/_esm/index.js"
22
+ },
23
+ "require": {
24
+ "types": "./dist/_types/index.d.ts",
25
+ "default": "./dist/_cjs/index.js"
26
+ },
27
+ "default": {
28
+ "types": "./dist/_types/index.d.ts",
29
+ "default": "./dist/_esm/index.js"
30
+ }
31
+ },
32
+ "./react-native": {
33
+ "browser": "./dist/_esm/stubs/native-on-web.js",
34
+ "import": {
35
+ "types": "./dist/_types/index.native.d.ts",
36
+ "default": "./dist/_esm/index.native.js"
37
+ },
38
+ "default": "./dist/_esm/index.native.js"
39
+ },
40
+ "./react-native/stampers/secure-store": {
41
+ "browser": "./dist/_esm/stubs/native-on-web.js",
42
+ "import": {
43
+ "types": "./dist/_types/native/stampers/secureStore.d.ts",
44
+ "default": "./dist/_esm/native/stampers/secureStore.js"
45
+ },
46
+ "default": "./dist/_esm/native/stampers/secureStore.js"
47
+ },
48
+ "./react-native/stampers/passkey": {
49
+ "browser": "./dist/_esm/stubs/native-on-web.js",
50
+ "import": {
51
+ "types": "./dist/_types/native/stampers/passkey.d.ts",
52
+ "default": "./dist/_esm/native/stampers/passkey.js"
53
+ },
54
+ "default": "./dist/_esm/native/stampers/passkey.js"
55
+ },
56
+ "./react-native/storage/async-storage": {
57
+ "browser": "./dist/_esm/stubs/native-on-web.js",
58
+ "import": {
59
+ "types": "./dist/_types/native/storage/asyncStorage.d.ts",
60
+ "default": "./dist/_esm/native/storage/asyncStorage.js"
61
+ },
62
+ "default": "./dist/_esm/native/storage/asyncStorage.js"
13
63
  },
14
64
  "./viem": {
15
65
  "types": "./dist/_types/adapters/viem.d.ts",
@@ -20,11 +70,6 @@
20
70
  "types": "./dist/_types/actions/index.d.ts",
21
71
  "import": "./dist/_esm/actions/index.js",
22
72
  "require": "./dist/_cjs/actions/index.js"
23
- },
24
- "./stampers": {
25
- "types": "./dist/_types/stampers/index.d.ts",
26
- "import": "./dist/_esm/stampers/index.js",
27
- "require": "./dist/_cjs/stampers/index.js"
28
73
  }
29
74
  },
30
75
  "files": [
@@ -59,12 +104,48 @@
59
104
  "json-canonicalize": "^2.0.0"
60
105
  },
61
106
  "peerDependencies": {
62
- "viem": "^2.38.0"
107
+ "viem": "^2.38.0",
108
+ "@react-native-async-storage/async-storage": ">=2.0.0",
109
+ "@turnkey/api-key-stamper": ">=0.6.0",
110
+ "@turnkey/crypto": ">=2.8.0",
111
+ "@turnkey/react-native-passkey-stamper": ">=1.2.0",
112
+ "expo-secure-store": ">=14.0.0",
113
+ "react-native": ">=0.73.0",
114
+ "uuid": ">=11.0.0"
115
+ },
116
+ "peerDependenciesMeta": {
117
+ "@react-native-async-storage/async-storage": {
118
+ "optional": true
119
+ },
120
+ "@turnkey/api-key-stamper": {
121
+ "optional": true
122
+ },
123
+ "@turnkey/crypto": {
124
+ "optional": true
125
+ },
126
+ "@turnkey/react-native-passkey-stamper": {
127
+ "optional": true
128
+ },
129
+ "expo-secure-store": {
130
+ "optional": true
131
+ },
132
+ "react-native": {
133
+ "optional": true
134
+ },
135
+ "uuid": {
136
+ "optional": true
137
+ }
63
138
  },
64
139
  "devDependencies": {
65
140
  "@hpke/core": "^1.9.0",
141
+ "@react-native-async-storage/async-storage": "2.2.0",
142
+ "@turnkey/api-key-stamper": "0.6.4",
143
+ "@turnkey/crypto": "2.8.13",
144
+ "@turnkey/react-native-passkey-stamper": "1.2.12",
66
145
  "@types/node": "^20.0.0",
67
- "typescript": "5.9.3"
146
+ "expo-secure-store": "55.0.9",
147
+ "typescript": "5.9.3",
148
+ "uuid": "^13.0.0"
68
149
  },
69
150
  "scripts": {
70
151
  "build": "pnpm run clean && pnpm run build:cjs && pnpm run build:esm && pnpm run build:types",
@@ -7,6 +7,8 @@ export type AuthenticateWithOAuthParameters = {
7
7
  projectId: string
8
8
  /** The session ID from the OAuth callback URL */
9
9
  sessionId: string
10
+ /** Proof-of-possession signature for `sessionId`. */
11
+ popSignature: string
10
12
  }
11
13
 
12
14
  export type AuthenticateWithOAuthReturnType = {
@@ -37,6 +39,7 @@ export type AuthenticateWithOAuthReturnType = {
37
39
  * provider: 'google',
38
40
  * projectId: 'proj_456',
39
41
  * sessionId: 'abc123',
42
+ * popSignature: '3045022100...',
40
43
  * });
41
44
  * ```
42
45
  */
@@ -44,11 +47,11 @@ export async function authenticateWithOAuth(
44
47
  client: Client,
45
48
  params: AuthenticateWithOAuthParameters,
46
49
  ): Promise<AuthenticateWithOAuthReturnType> {
47
- const { projectId, sessionId } = params
50
+ const { projectId, sessionId, popSignature } = params
48
51
 
49
52
  return await client.request({
50
53
  path: `${projectId}/auth/oauth`,
51
54
  method: 'POST',
52
- body: { sessionId },
55
+ body: { sessionId, popSignature },
53
56
  })
54
57
  }
@@ -10,7 +10,10 @@ export {
10
10
  createClient,
11
11
  type ZeroDevWalletClient,
12
12
  } from './createClient.js'
13
- export { zeroDevWalletTransport } from './transports/createTransport.js'
13
+ export {
14
+ type CreateTransportOptions,
15
+ zeroDevWalletTransport,
16
+ } from './transports/createTransport.js'
14
17
  export type {
15
18
  Client,
16
19
  ClientConfig,
@@ -10,6 +10,8 @@ export type CreateTransportOptions = {
10
10
  key?: string
11
11
  /** Transport name */
12
12
  name?: string
13
+ /** Extra options merged into every fetch() call */
14
+ fetchOptions?: Omit<RequestInit, 'body' | 'method' | 'signal'>
13
15
  }
14
16
 
15
17
  /**
@@ -34,6 +36,7 @@ export function zeroDevWalletTransport(
34
36
  name,
35
37
  apiKeyStamper,
36
38
  passkeyStamper,
39
+ ...(options.fetchOptions && { fetchOptions: options.fetchOptions }),
37
40
  })
38
41
 
39
42
  return {
@@ -50,9 +50,9 @@ export function rest(url: string, cfg: RestTransportConfig): RestTransport {
50
50
  try {
51
51
  let requestBody = args.body
52
52
  let requestHeaders = {
53
- 'content-type': 'application/json',
54
- ...(args.headers ?? {}),
55
53
  ...(cfg.fetchOptions?.headers ?? {}),
54
+ ...(args.headers ?? {}),
55
+ 'content-type': 'application/json',
56
56
  }
57
57
 
58
58
  // Handle stamping if requested
@@ -5,6 +5,7 @@ import type {
5
5
  } from '../actions/auth/index.js'
6
6
  import { toViemAccount } from '../adapters/viem.js'
7
7
  import {
8
+ type CreateTransportOptions,
8
9
  createAuthProxyClient,
9
10
  createClient,
10
11
  type ZeroDevWalletClient,
@@ -15,10 +16,7 @@ import {
15
16
  DEFAULT_SESSION_EXPIRATION_IN_SECONDS,
16
17
  KMS_SERVER_URL,
17
18
  } from '../constants.js'
18
- import { createIndexedDbStamper } from '../stampers/indexedDbStamper.js'
19
19
  import type { ApiKeyStamper, PasskeyStamper } from '../stampers/types.js'
20
- import { createWebauthnStamper } from '../stampers/webauthnStamper.js'
21
- import { createWebStorageAdapter } from '../storage/adapters.js'
22
20
  import {
23
21
  createStorageManager,
24
22
  type StorageAdapter,
@@ -27,14 +25,15 @@ import { SessionType, type ZeroDevWalletSession } from '../types/session.js'
27
25
  import { buildClientSignature } from '../utils/buildClientSignature.js'
28
26
  import { encryptOtpAttempt } from '../utils/encryptOtpAttempt.js'
29
27
  import { humanReadableDateTime, parseSession } from '../utils/utils.js'
30
- export interface ZeroDevWalletConfig {
28
+ export interface ZeroDevWalletConfigCore {
31
29
  organizationId?: string
32
30
  proxyBaseUrl?: string
33
31
  projectId: string
34
- sessionStorage?: StorageAdapter
35
- rpId?: string
36
- apiKeyStamper?: ApiKeyStamper
37
- passkeyStamper?: PasskeyStamper
32
+ sessionStorage: StorageAdapter
33
+ rpId: string
34
+ apiKeyStamper: ApiKeyStamper
35
+ passkeyStamper: PasskeyStamper
36
+ fetchOptions?: CreateTransportOptions['fetchOptions']
38
37
  }
39
38
 
40
39
  // Re-export EmailCustomization for convenience
@@ -116,30 +115,26 @@ export interface ZeroDevWalletSDK {
116
115
  toAccount: () => Promise<LocalAccount>
117
116
  }
118
117
 
119
- export async function createZeroDevWallet(
120
- config: ZeroDevWalletConfig,
118
+ export async function createZeroDevWalletCore(
119
+ config: ZeroDevWalletConfigCore,
121
120
  ): Promise<ZeroDevWalletSDK> {
122
121
  const {
123
122
  projectId,
124
123
  sessionStorage,
125
- rpId = window.location.hostname,
124
+ rpId,
125
+ apiKeyStamper,
126
+ passkeyStamper,
126
127
  organizationId = DEFAULT_ORGANIZATION_ID,
127
128
  } = config
128
129
 
129
- const sessionStorageManager = createStorageManager(
130
- sessionStorage || createWebStorageAdapter(),
131
- )
132
-
133
- const apiKeyStamper = config.apiKeyStamper ?? (await createIndexedDbStamper())
134
-
135
- const passkeyStamper =
136
- config.passkeyStamper ?? (await createWebauthnStamper({ rpId }))
130
+ const sessionStorageManager = createStorageManager(sessionStorage)
137
131
 
138
132
  const client = createClient({
139
133
  apiKeyStamper,
140
134
  passkeyStamper,
141
135
  transport: zeroDevWalletTransport({
142
136
  baseUrl: config.proxyBaseUrl || `${KMS_SERVER_URL}/api/v1`,
137
+ ...(config.fetchOptions && { fetchOptions: config.fetchOptions }),
143
138
  }),
144
139
  })
145
140
 
@@ -220,10 +215,12 @@ export async function createZeroDevWallet(
220
215
  async auth(params: AuthParams) {
221
216
  switch (params.type) {
222
217
  case 'oauth': {
218
+ const popSignature = await client.apiKeyStamper.sign(params.sessionId)
223
219
  const data = await client.authenticateWithOAuth({
224
220
  provider: params.provider,
225
221
  projectId,
226
222
  sessionId: params.sessionId,
223
+ popSignature,
227
224
  })
228
225
 
229
226
  if (data.session) {
@@ -0,0 +1,102 @@
1
+ import { isReactNative } from './utils/platform.js'
2
+
3
+ if (!isReactNative()) {
4
+ // biome-ignore lint/suspicious/noConsole: Warning users if they try to use the web entry on React Native.
5
+ console.warn(
6
+ '@zerodev/wallet-core/react-native: the React Native entry was loaded outside a React Native runtime. If this is a non-RN context, import `@zerodev/wallet-core` (the bare specifier) instead.',
7
+ )
8
+ }
9
+
10
+ // Re-export shared API surface. Note: we intentionally do NOT re-export the
11
+ // web stamper factories (createIndexedDbStamper, createWebauthnStamper,
12
+ // createIframeStamper) here — they're web-only. The native adapter factories
13
+ // live behind their own granular subpaths so consumers using alternative
14
+ // adapters (Keychain, MMKV, custom) skip the unused peer-dep installs.
15
+ export type {
16
+ ApiKeyAuthenticator,
17
+ AuthenticateWithEmailParameters,
18
+ AuthenticateWithEmailReturnType,
19
+ AuthenticateWithOAuthParameters,
20
+ AuthenticateWithOAuthReturnType,
21
+ EmailContact,
22
+ EmailCustomization,
23
+ GetAuthenticatorsParameters,
24
+ GetAuthenticatorsReturnType,
25
+ GetUserWalletParameters,
26
+ GetUserWalletReturnType,
27
+ GetWhoamiParameters,
28
+ GetWhoamiReturnType,
29
+ LoginWithOTPParameters,
30
+ LoginWithOTPReturnType,
31
+ OAuthAuthenticator,
32
+ OtpContact,
33
+ PasskeyAuthenticator,
34
+ RegisterWithOTPParameters,
35
+ RegisterWithOTPReturnType,
36
+ Sign7702AuthorizationParameters,
37
+ Sign7702AuthorizationReturnType,
38
+ SignMessageParameters,
39
+ SignMessageReturnType,
40
+ SignTransactionParameters,
41
+ SignTransactionReturnType,
42
+ SignTypedDataV4Parameters,
43
+ SignTypedDataV4ReturnType,
44
+ SignUserOperationParameters,
45
+ SignUserOperationReturnType,
46
+ } from './actions/index.js'
47
+ export {
48
+ authenticateWithEmail,
49
+ authenticateWithOAuth,
50
+ getAuthenticators,
51
+ getUserWallet,
52
+ getWhoami,
53
+ loginWithOTP,
54
+ registerWithOTP,
55
+ sign7702Authorization,
56
+ signMessage,
57
+ signTransaction,
58
+ signTypedDataV4,
59
+ signUserOperation,
60
+ } from './actions/index.js'
61
+ export type { ToViemAccountParams } from './adapters/viem.js'
62
+ export { toViemAccount } from './adapters/viem.js'
63
+ export type { ZeroDevWalletActions } from './client/decorators/client.js'
64
+ export { zeroDevWalletActions } from './client/decorators/client.js'
65
+ export type {
66
+ Client,
67
+ ClientConfig,
68
+ CreateTransportOptions,
69
+ Transport,
70
+ } from './client/index.js'
71
+ export {
72
+ createBaseClient,
73
+ createClient,
74
+ type ZeroDevWalletClient,
75
+ zeroDevWalletTransport,
76
+ } from './client/index.js'
77
+ export { KMS_SERVER_URL } from './constants.js'
78
+ export type {
79
+ AuthParams,
80
+ ZeroDevWalletSDK,
81
+ } from './core/createZeroDevWalletCore.js'
82
+ // RN entry re-exports core directly. `ZeroDevWalletConfigCore` already has
83
+ // all four adapter fields required, so RN consumers get compile-time enforcement
84
+ // without a separate type wrapper.
85
+ export {
86
+ createZeroDevWalletCore as createZeroDevWallet,
87
+ type ZeroDevWalletConfigCore as ZeroDevWalletConfig,
88
+ } from './core/createZeroDevWalletCore.js'
89
+ export type {
90
+ ApiKeyStamper,
91
+ Attestation,
92
+ IframeStamper,
93
+ PasskeyRegistrationOptions,
94
+ PasskeyRegistrationResult,
95
+ PasskeyStamper,
96
+ } from './stampers/types.js'
97
+ export type { StorageAdapter, StorageManager } from './storage/manager.js'
98
+ export type { StamperType, ZeroDevWalletSession } from './types/session.js'
99
+ export type { KeyFormat } from './utils/exportPrivateKey.js'
100
+ export { exportPrivateKey } from './utils/exportPrivateKey.js'
101
+ export { exportWallet } from './utils/exportWallet.js'
102
+ export { normalizeTimestamp } from './utils/utils.js'
package/src/index.ts CHANGED
@@ -1,3 +1,12 @@
1
+ import { isReactNative } from './utils/platform.js'
2
+
3
+ if (isReactNative()) {
4
+ // biome-ignore lint/suspicious/noConsole: Warning users if they try to use the web entry on React Native.
5
+ console.warn(
6
+ '@zerodev/wallet-core: the web entry was loaded in a React Native runtime. Check that your metro.config.js has `unstable_enablePackageExports: true` and `"react-native"` in `unstable_conditionNames`.',
7
+ )
8
+ }
9
+
1
10
  export type {
2
11
  // Auth types
3
12
  ApiKeyAuthenticator,
@@ -56,7 +65,12 @@ export { toViemAccount } from './adapters/viem.js'
56
65
  export type { ZeroDevWalletActions } from './client/decorators/client.js'
57
66
  // Client decorators
58
67
  export { zeroDevWalletActions } from './client/decorators/client.js'
59
- export type { Client, ClientConfig, Transport } from './client/index.js'
68
+ export type {
69
+ Client,
70
+ ClientConfig,
71
+ CreateTransportOptions,
72
+ Transport,
73
+ } from './client/index.js'
60
74
  // Client
61
75
  export {
62
76
  createBaseClient,
@@ -68,17 +82,10 @@ export {
68
82
  export { KMS_SERVER_URL } from './constants.js'
69
83
  export type {
70
84
  AuthParams,
71
- ZeroDevWalletConfig,
72
85
  ZeroDevWalletSDK,
73
- } from './core/createZeroDevWallet.js'
74
- // Core
75
- export { createZeroDevWallet } from './core/createZeroDevWallet.js'
86
+ } from './core/createZeroDevWalletCore.js'
76
87
  // Stampers
77
- export {
78
- createIframeStamper,
79
- createIndexedDbStamper,
80
- createWebauthnStamper,
81
- } from './stampers/index.js'
88
+ export { createIframeStamper } from './stampers/iframeStamper.js'
82
89
  export type {
83
90
  ApiKeyStamper,
84
91
  Attestation,
@@ -96,3 +103,11 @@ export { exportPrivateKey } from './utils/exportPrivateKey.js'
96
103
  export { exportWallet } from './utils/exportWallet.js'
97
104
  // Utils
98
105
  export { normalizeTimestamp } from './utils/utils.js'
106
+ // Core — bare specifier resolves web defaults (IndexedDB, WebAuthn, session
107
+ // storage, hostname-derived rpId) before forwarding to the shared factory.
108
+ // RN consumers go through `@zerodev/wallet-core/react-native`, which exposes
109
+ // the strict `ZeroDevWalletConfig` (= core's required-fields shape) directly.
110
+ export {
111
+ createZeroDevWallet,
112
+ type ZeroDevWalletConfig,
113
+ } from './web/createZeroDevWallet.js'
@@ -0,0 +1,39 @@
1
+ import {
2
+ createPasskey,
3
+ PasskeyStamper as TurnkeyPasskeyStamper,
4
+ } from '@turnkey/react-native-passkey-stamper'
5
+ import { v4 as uuidv4 } from 'uuid'
6
+ import type {
7
+ PasskeyRegistrationOptions,
8
+ PasskeyStamper,
9
+ } from '../../stampers/types.js'
10
+
11
+ export async function createReactNativePasskeyStamper({
12
+ rpId,
13
+ }: {
14
+ rpId: string
15
+ }): Promise<PasskeyStamper> {
16
+ const inner = new TurnkeyPasskeyStamper({ rpId })
17
+
18
+ return {
19
+ async stamp(payload: string) {
20
+ return await inner.stamp(payload)
21
+ },
22
+ async clear() {},
23
+ async register({ rp, userName }: PasskeyRegistrationOptions) {
24
+ const { attestation, challenge: encodedChallenge } = await createPasskey({
25
+ rp: {
26
+ id: rp.id,
27
+ name: rp.name ?? 'Turnkey',
28
+ },
29
+ user: {
30
+ id: uuidv4(),
31
+ name: userName,
32
+ displayName: userName,
33
+ },
34
+ authenticatorName: userName ?? 'End-User Passkey',
35
+ })
36
+ return { attestation, encodedChallenge }
37
+ },
38
+ }
39
+ }
@@ -0,0 +1,139 @@
1
+ import { ApiKeyStamper, SignatureFormat } from '@turnkey/api-key-stamper'
2
+ import { generateP256KeyPair } from '@turnkey/crypto'
3
+ import * as SecureStore from 'expo-secure-store'
4
+ import type { ApiKeyStamper as ZDApiKeyStamper } from '../../stampers/types.js'
5
+
6
+ const PUBLIC_KEY = 'zerodev.publicKey'
7
+ const PRIVATE_KEY = 'zerodev.privateKey'
8
+
9
+ class SecureStoreStamperInner {
10
+ private publicKeyHex: string | null = null
11
+
12
+ async init(): Promise<void> {
13
+ const publicKey = await SecureStore.getItemAsync(PUBLIC_KEY)
14
+ const privateKey = await SecureStore.getItemAsync(PRIVATE_KEY)
15
+
16
+ if (publicKey && privateKey) {
17
+ this.publicKeyHex = publicKey
18
+ } else {
19
+ await this.resetKeyPair()
20
+ }
21
+ }
22
+
23
+ async getPublicKey(): Promise<string | null> {
24
+ return this.publicKeyHex
25
+ }
26
+
27
+ async resetKeyPair(externalKeyPair?: {
28
+ publicKey: string
29
+ privateKey: string
30
+ }): Promise<void> {
31
+ await this.clear()
32
+
33
+ const pair = externalKeyPair ?? generateP256KeyPair()
34
+
35
+ await SecureStore.setItemAsync(PUBLIC_KEY, pair.publicKey)
36
+ await SecureStore.setItemAsync(PRIVATE_KEY, pair.privateKey)
37
+
38
+ this.publicKeyHex = pair.publicKey
39
+ }
40
+
41
+ private async getTurnkeyApiKeyStamper(): Promise<ApiKeyStamper> {
42
+ if (!this.publicKeyHex) {
43
+ throw new Error(
44
+ 'Key not initialized. Call init() or resetKeyPair() first.',
45
+ )
46
+ }
47
+
48
+ const privateKey = await SecureStore.getItemAsync(PRIVATE_KEY)
49
+ if (!privateKey) {
50
+ throw new Error('No private key found in secure store.')
51
+ }
52
+
53
+ return new ApiKeyStamper({
54
+ apiPublicKey: this.publicKeyHex,
55
+ apiPrivateKey: privateKey,
56
+ })
57
+ }
58
+
59
+ async stamp(
60
+ payload: string,
61
+ ): Promise<{ stampHeaderName: string; stampHeaderValue: string }> {
62
+ const stamper = await this.getTurnkeyApiKeyStamper()
63
+ const { stampHeaderName, stampHeaderValue } = await stamper.stamp(payload)
64
+ return { stampHeaderName, stampHeaderValue }
65
+ }
66
+
67
+ async sign(payload: string): Promise<string> {
68
+ const stamper = await this.getTurnkeyApiKeyStamper()
69
+ return stamper.sign(payload, SignatureFormat.Der)
70
+ }
71
+
72
+ async clear(): Promise<void> {
73
+ await SecureStore.deleteItemAsync(PUBLIC_KEY)
74
+ await SecureStore.deleteItemAsync(PRIVATE_KEY)
75
+ this.publicKeyHex = null
76
+ }
77
+ }
78
+
79
+ async function warmApiKeyStamperForMetroDev(
80
+ inner: SecureStoreStamperInner,
81
+ ): Promise<void> {
82
+ // `__DEV__` is a global set by React Native's Metro; read it via globalThis
83
+ // so this file needs no ambient `.d.ts` (which would conflict with RN's own
84
+ // typings when both happen to be in scope, e.g. in the editor).
85
+ if (!(globalThis as { __DEV__?: boolean }).__DEV__) return
86
+
87
+ // In Expo dev, Turnkey's API key stamper loads its React Native signer with a
88
+ // dynamic import the first time `stamp()` runs. OTP verification is usually
89
+ // the first code path that stamps a payload, and if the app was backgrounded
90
+ // while the user copied the code, Metro can serve that lazy module after
91
+ // foregrounding and trigger a full JS reload. Warming the stamper during dev
92
+ // startup makes Metro load the signer while the app is foregrounded; production
93
+ // builds do not use Metro and skip this block via `__DEV__`.
94
+ try {
95
+ await inner.stamp('{"purpose":"metro-dev-warmup"}')
96
+ } catch (error) {
97
+ // biome-ignore lint/suspicious/noConsole: This only runs in dev mode
98
+ console.warn('Failed to warm API key stamper in dev:', error)
99
+ }
100
+ }
101
+
102
+ export async function createSecureStoreStamper(): Promise<ZDApiKeyStamper> {
103
+ const inner = new SecureStoreStamperInner()
104
+ await inner.init()
105
+ await warmApiKeyStamperForMetroDev(inner)
106
+
107
+ let pendingKeyPair: { publicKey: string; privateKey: string } | null = null
108
+
109
+ return {
110
+ async getPublicKey() {
111
+ return inner.getPublicKey()
112
+ },
113
+ async stamp(payload: string) {
114
+ return inner.stamp(payload)
115
+ },
116
+ async sign(payload: string) {
117
+ return inner.sign(payload)
118
+ },
119
+ async clear() {
120
+ await inner.clear()
121
+ },
122
+ async resetKeyPair() {
123
+ pendingKeyPair = null
124
+ await inner.resetKeyPair()
125
+ },
126
+ async prepareKeyRotation() {
127
+ const keyPair = generateP256KeyPair()
128
+ pendingKeyPair = keyPair
129
+ return keyPair.publicKey
130
+ },
131
+ async commitKeyRotation() {
132
+ if (!pendingKeyPair) {
133
+ throw new Error('No pending key rotation to commit')
134
+ }
135
+ await inner.resetKeyPair(pendingKeyPair)
136
+ pendingKeyPair = null
137
+ },
138
+ }
139
+ }
@@ -0,0 +1,18 @@
1
+ import type { AsyncStorageStatic } from '@react-native-async-storage/async-storage'
2
+ import * as AsyncStorageNS from '@react-native-async-storage/async-storage'
3
+ import type { StorageAdapter } from '../../storage/manager.js'
4
+
5
+ // CJS package whose `.d.ts` declares `export default AsyncStorage`. Our tsconfig
6
+ // keeps `esModuleInterop: false` (and `verbatimModuleSyntax: true`), so a plain
7
+ // `import AsyncStorage from '...'` types as the module namespace instead of
8
+ // the default. Unwrap explicitly. At runtime Metro returns the default object
9
+ // either way; this only affects the static type.
10
+ const AsyncStorage: AsyncStorageStatic = (
11
+ AsyncStorageNS as unknown as { default: AsyncStorageStatic }
12
+ ).default
13
+
14
+ export const asyncStorageAdapter: StorageAdapter = {
15
+ getItem: (key) => AsyncStorage.getItem(key),
16
+ setItem: (key, value) => AsyncStorage.setItem(key, value),
17
+ removeItem: (key) => AsyncStorage.removeItem(key),
18
+ }
@@ -15,6 +15,9 @@ export async function createIndexedDbStamper(): Promise<ApiKeyStamper> {
15
15
  async stamp(payload: string) {
16
16
  return await inner.stamp(payload)
17
17
  },
18
+ async sign(payload: string) {
19
+ return await inner.sign(payload)
20
+ },
18
21
  async clear() {
19
22
  await inner.clear()
20
23
  },
@@ -39,6 +39,11 @@ export type ApiKeyStamper = Stamper & {
39
39
  prepareKeyRotation: () => Promise<string>
40
40
  /** Promote the pending key to active. Call after the server accepts the new key. */
41
41
  commitKeyRotation: () => Promise<void>
42
+ /**
43
+ * Sign `payload` with the currently active key. Returns a hex-encoded
44
+ * ECDSA-P256 / SHA-256 signature in ASN.1 DER form.
45
+ */
46
+ sign: (payload: string) => Promise<string>
42
47
  }
43
48
  export type Attestation = {
44
49
  attestationObject: string
@@ -0,0 +1,3 @@
1
+ throw new Error(
2
+ '@zerodev/wallet-core react-native subpaths cannot be imported in a web environment. Use the bare specifier (`import { ... } from "@zerodev/wallet-core"`) instead.',
3
+ )