@0xsequence/wallet-wdk 3.0.0-beta.8 → 3.0.0

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 (113) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-lint.log +4 -0
  3. package/.turbo/turbo-typecheck.log +4 -0
  4. package/CHANGELOG.md +182 -0
  5. package/dist/dbs/auth-commitments.d.ts.map +1 -1
  6. package/dist/dbs/auth-keys.d.ts +3 -1
  7. package/dist/dbs/auth-keys.d.ts.map +1 -1
  8. package/dist/dbs/auth-keys.js +16 -4
  9. package/dist/dbs/messages.d.ts.map +1 -1
  10. package/dist/dbs/passkey-credentials.d.ts.map +1 -1
  11. package/dist/dbs/recovery.d.ts.map +1 -1
  12. package/dist/dbs/signatures.d.ts.map +1 -1
  13. package/dist/dbs/transactions.d.ts.map +1 -1
  14. package/dist/dbs/wallets.d.ts.map +1 -1
  15. package/dist/env.d.ts +22 -0
  16. package/dist/env.d.ts.map +1 -0
  17. package/dist/env.js +30 -0
  18. package/dist/identity/signer.d.ts +5 -4
  19. package/dist/identity/signer.d.ts.map +1 -1
  20. package/dist/identity/signer.js +11 -4
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/sequence/cron.d.ts +3 -0
  25. package/dist/sequence/cron.d.ts.map +1 -1
  26. package/dist/sequence/cron.js +72 -39
  27. package/dist/sequence/handlers/authcode-pkce.d.ts +2 -1
  28. package/dist/sequence/handlers/authcode-pkce.d.ts.map +1 -1
  29. package/dist/sequence/handlers/authcode-pkce.js +4 -4
  30. package/dist/sequence/handlers/authcode.d.ts +4 -1
  31. package/dist/sequence/handlers/authcode.d.ts.map +1 -1
  32. package/dist/sequence/handlers/authcode.js +23 -6
  33. package/dist/sequence/handlers/devices.d.ts +1 -1
  34. package/dist/sequence/handlers/devices.d.ts.map +1 -1
  35. package/dist/sequence/handlers/devices.js +1 -1
  36. package/dist/sequence/handlers/guard.d.ts +1 -1
  37. package/dist/sequence/handlers/guard.d.ts.map +1 -1
  38. package/dist/sequence/handlers/guard.js +22 -19
  39. package/dist/sequence/handlers/identity.d.ts +3 -1
  40. package/dist/sequence/handlers/identity.d.ts.map +1 -1
  41. package/dist/sequence/handlers/identity.js +14 -7
  42. package/dist/sequence/handlers/mnemonic.d.ts.map +1 -1
  43. package/dist/sequence/handlers/mnemonic.js +21 -18
  44. package/dist/sequence/handlers/otp.d.ts +2 -1
  45. package/dist/sequence/handlers/otp.d.ts.map +1 -1
  46. package/dist/sequence/handlers/otp.js +4 -3
  47. package/dist/sequence/handlers/passkeys.d.ts +6 -4
  48. package/dist/sequence/handlers/passkeys.d.ts.map +1 -1
  49. package/dist/sequence/handlers/passkeys.js +8 -5
  50. package/dist/sequence/handlers/recovery.js +1 -1
  51. package/dist/sequence/index.d.ts +2 -0
  52. package/dist/sequence/index.d.ts.map +1 -1
  53. package/dist/sequence/index.js +1 -0
  54. package/dist/sequence/manager.d.ts +67 -55
  55. package/dist/sequence/manager.d.ts.map +1 -1
  56. package/dist/sequence/manager.js +77 -17
  57. package/dist/sequence/messages.js +1 -1
  58. package/dist/sequence/passkeys-provider.d.ts +24 -0
  59. package/dist/sequence/passkeys-provider.d.ts.map +1 -0
  60. package/dist/sequence/passkeys-provider.js +15 -0
  61. package/dist/sequence/recovery.d.ts +2 -0
  62. package/dist/sequence/recovery.d.ts.map +1 -1
  63. package/dist/sequence/recovery.js +100 -34
  64. package/dist/sequence/signers.d.ts.map +1 -1
  65. package/dist/sequence/signers.js +3 -1
  66. package/dist/sequence/transactions.d.ts.map +1 -1
  67. package/dist/sequence/transactions.js +5 -2
  68. package/dist/sequence/wallets.d.ts +2 -1
  69. package/dist/sequence/wallets.d.ts.map +1 -1
  70. package/dist/sequence/wallets.js +32 -22
  71. package/eslint.config.js +12 -0
  72. package/package.json +16 -14
  73. package/src/dbs/auth-commitments.ts +1 -1
  74. package/src/dbs/auth-keys.ts +20 -6
  75. package/src/dbs/messages.ts +1 -1
  76. package/src/dbs/passkey-credentials.ts +1 -1
  77. package/src/dbs/recovery.ts +1 -1
  78. package/src/dbs/signatures.ts +1 -1
  79. package/src/dbs/transactions.ts +1 -1
  80. package/src/dbs/wallets.ts +1 -1
  81. package/src/env.ts +58 -0
  82. package/src/identity/signer.ts +13 -7
  83. package/src/index.ts +1 -0
  84. package/src/sequence/cron.ts +75 -42
  85. package/src/sequence/handlers/authcode-pkce.ts +6 -4
  86. package/src/sequence/handlers/authcode.ts +26 -5
  87. package/src/sequence/handlers/devices.ts +1 -1
  88. package/src/sequence/handlers/guard.ts +6 -4
  89. package/src/sequence/handlers/identity.ts +18 -8
  90. package/src/sequence/handlers/mnemonic.ts +5 -3
  91. package/src/sequence/handlers/otp.ts +5 -3
  92. package/src/sequence/handlers/passkeys.ts +13 -13
  93. package/src/sequence/handlers/recovery.ts +1 -1
  94. package/src/sequence/index.ts +2 -0
  95. package/src/sequence/manager.ts +168 -14
  96. package/src/sequence/messages.ts +1 -1
  97. package/src/sequence/passkeys-provider.ts +55 -0
  98. package/src/sequence/recovery.ts +165 -56
  99. package/src/sequence/signers.ts +3 -1
  100. package/src/sequence/transactions.ts +6 -2
  101. package/src/sequence/wallets.ts +39 -25
  102. package/test/authcode-pkce.test.ts +2 -3
  103. package/test/authcode.test.ts +6 -8
  104. package/test/constants.ts +4 -2
  105. package/test/guard.test.ts +5 -5
  106. package/test/identity-signer.test.ts +1 -1
  107. package/test/otp.test.ts +1 -1
  108. package/test/passkeys.test.ts +1 -1
  109. package/test/recovery.test.ts +3 -3
  110. package/test/sessions.test.ts +1 -1
  111. package/test/{test-ssr-safety.mjs → test-ssr-safety.js} +143 -137
  112. package/test/transactions.test.ts +3 -3
  113. package/test/wallets.test.ts +5 -5
package/src/env.ts ADDED
@@ -0,0 +1,58 @@
1
+ import type { CoreEnv } from '@0xsequence/wallet-core'
2
+ import { resolveCoreEnv } from '@0xsequence/wallet-core'
3
+
4
+ export type TimersLike = {
5
+ setTimeout: typeof setTimeout
6
+ clearTimeout: typeof clearTimeout
7
+ setInterval: typeof setInterval
8
+ clearInterval: typeof clearInterval
9
+ }
10
+
11
+ export type LockManagerLike = {
12
+ request: (name: string, callback: (lock: Lock | null) => Promise<void> | void) => Promise<void>
13
+ }
14
+
15
+ export type NavigationLike = {
16
+ getPathname: () => string
17
+ redirect: (url: string) => void
18
+ }
19
+
20
+ export type WdkEnv = CoreEnv & {
21
+ timers?: TimersLike
22
+ locks?: LockManagerLike
23
+ navigation?: NavigationLike
24
+ urlSearchParams?: typeof URLSearchParams
25
+ }
26
+
27
+ export function resolveWdkEnv(env?: WdkEnv): WdkEnv {
28
+ const core = resolveCoreEnv(env)
29
+ const globalObj = globalThis as any
30
+ const windowObj = typeof window !== 'undefined' ? window : (globalObj.window ?? {})
31
+ const location = windowObj.location ?? globalObj.location
32
+
33
+ return {
34
+ ...core,
35
+ timers:
36
+ env?.timers ??
37
+ (typeof globalObj.setTimeout === 'function'
38
+ ? {
39
+ setTimeout: globalObj.setTimeout.bind(globalObj),
40
+ clearTimeout: globalObj.clearTimeout.bind(globalObj),
41
+ setInterval: globalObj.setInterval.bind(globalObj),
42
+ clearInterval: globalObj.clearInterval.bind(globalObj),
43
+ }
44
+ : undefined),
45
+ locks: env?.locks ?? globalObj.navigator?.locks ?? windowObj.navigator?.locks,
46
+ navigation:
47
+ env?.navigation ??
48
+ (location
49
+ ? {
50
+ getPathname: () => location.pathname,
51
+ redirect: (url: string) => {
52
+ location.href = url
53
+ },
54
+ }
55
+ : undefined),
56
+ urlSearchParams: env?.urlSearchParams ?? globalObj.URLSearchParams ?? windowObj.URLSearchParams,
57
+ }
58
+ }
@@ -1,17 +1,22 @@
1
- import { Address, Signature, Hex, Bytes, PersonalMessage } from 'ox'
2
- import { Signers, State } from '@0xsequence/wallet-core'
3
- import { IdentityInstrument, KeyType } from '@0xsequence/identity-instrument'
1
+ import { Address, Signature, Hex, Bytes } from 'ox'
2
+ import { Signers, State, type CryptoLike } from '@0xsequence/wallet-core'
3
+ import { IdentityInstrument } from '@0xsequence/identity-instrument'
4
4
  import { AuthKey } from '../dbs/auth-keys.js'
5
5
  import { Payload, Signature as SequenceSignature } from '@0xsequence/wallet-primitives'
6
6
  import * as Identity from '@0xsequence/identity-instrument'
7
7
 
8
- export function toIdentityAuthKey(authKey: AuthKey): Identity.AuthKey {
8
+ export function toIdentityAuthKey(authKey: AuthKey, crypto?: CryptoLike): Identity.AuthKey {
9
+ const globalObj = globalThis as any
10
+ const resolvedCrypto = crypto ?? globalObj.window?.crypto ?? globalObj.crypto
11
+ if (!resolvedCrypto?.subtle) {
12
+ throw new Error('crypto.subtle is not available')
13
+ }
9
14
  return {
10
15
  address: authKey.address,
11
16
  keyType: Identity.KeyType.WebCrypto_Secp256r1,
12
17
  signer: authKey.identitySigner,
13
18
  async sign(digest: Bytes.Bytes) {
14
- const authKeySignature = await window.crypto.subtle.sign(
19
+ const authKeySignature = await resolvedCrypto.subtle.sign(
15
20
  {
16
21
  name: 'ECDSA',
17
22
  hash: 'SHA-256',
@@ -28,6 +33,7 @@ export class IdentitySigner implements Signers.Signer {
28
33
  constructor(
29
34
  readonly identityInstrument: IdentityInstrument,
30
35
  readonly authKey: AuthKey,
36
+ private readonly crypto?: CryptoLike,
31
37
  ) {}
32
38
 
33
39
  get address(): Address.Address {
@@ -47,7 +53,7 @@ export class IdentitySigner implements Signers.Signer {
47
53
  }
48
54
 
49
55
  async signDigest(digest: Bytes.Bytes): Promise<SequenceSignature.SignatureOfSignerLeafHash> {
50
- const sigHex = await this.identityInstrument.sign(toIdentityAuthKey(this.authKey), digest)
56
+ const sigHex = await this.identityInstrument.sign(toIdentityAuthKey(this.authKey, this.crypto), digest)
51
57
  const sig = Signature.fromHex(sigHex)
52
58
  return {
53
59
  type: 'hash',
@@ -55,7 +61,7 @@ export class IdentitySigner implements Signers.Signer {
55
61
  }
56
62
  }
57
63
 
58
- async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: Object): Promise<void> {
64
+ async witness(stateWriter: State.Writer, wallet: Address.Address, extra?: object): Promise<void> {
59
65
  const payload = Payload.fromMessage(
60
66
  Hex.fromString(
61
67
  JSON.stringify({
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * as Identity from './identity/signer.js'
2
2
  export * as Sequence from './sequence/index.js'
3
+ export * from './env.js'
@@ -17,12 +17,14 @@ export class Cron {
17
17
  private readonly STORAGE_KEY = 'sequence-cron-jobs'
18
18
  private isStopping: boolean = false
19
19
  private currentCheckJobsPromise: Promise<void> = Promise.resolve()
20
+ private readonly env: Shared['env']
20
21
 
21
22
  /**
22
23
  * Initializes the Cron scheduler and starts the periodic job checker.
23
24
  * @param shared Shared context for modules and logging.
24
25
  */
25
26
  constructor(private readonly shared: Shared) {
27
+ this.env = shared.env
26
28
  this.start()
27
29
  }
28
30
 
@@ -33,7 +35,11 @@ export class Cron {
33
35
  private start() {
34
36
  if (this.isStopping) return
35
37
  this.executeCheckJobsChain()
36
- this.checkInterval = setInterval(() => this.executeCheckJobsChain(), 60 * 1000)
38
+ const setIntervalFn = this.env.timers?.setInterval ?? (globalThis as any).setInterval
39
+ if (!setIntervalFn) {
40
+ return
41
+ }
42
+ this.checkInterval = setIntervalFn(() => this.executeCheckJobsChain(), 60 * 1000)
37
43
  }
38
44
 
39
45
  /**
@@ -58,7 +64,10 @@ export class Cron {
58
64
  this.isStopping = true
59
65
 
60
66
  if (this.checkInterval) {
61
- clearInterval(this.checkInterval)
67
+ const clearIntervalFn = this.env.timers?.clearInterval ?? (globalThis as any).clearInterval
68
+ if (clearIntervalFn) {
69
+ clearIntervalFn(this.checkInterval)
70
+ }
62
71
  this.checkInterval = undefined
63
72
  this.shared.modules.logger.log('Cron: Interval cleared.')
64
73
  }
@@ -104,48 +113,22 @@ export class Cron {
104
113
  }
105
114
 
106
115
  try {
107
- await navigator.locks.request('sequence-cron-jobs', async (lock: Lock | null) => {
108
- if (this.isStopping) {
109
- return
110
- }
111
- if (!lock) {
112
- return
113
- }
114
-
115
- const now = Date.now()
116
- const storage = await this.getStorageState()
117
-
118
- for (const [id, job] of this.jobs) {
116
+ const locks = this.env.locks ?? (globalThis as any).navigator?.locks
117
+ if (locks?.request) {
118
+ await locks.request('sequence-cron-jobs', async (lock: Lock | null) => {
119
119
  if (this.isStopping) {
120
- break
120
+ return
121
121
  }
122
-
123
- const lastRun = storage.get(id)?.lastRun ?? job.lastRun
124
- const timeSinceLastRun = now - lastRun
125
-
126
- if (timeSinceLastRun >= job.interval) {
127
- try {
128
- await job.handler()
129
- if (!this.isStopping) {
130
- job.lastRun = now
131
- storage.set(id, { lastRun: now })
132
- }
133
- } catch (error) {
134
- if (error instanceof DOMException && error.name === 'AbortError') {
135
- this.shared.modules.logger.log(`Cron: Job ${id} was aborted.`)
136
- } else {
137
- console.error(`Cron job ${id} failed:`, error)
138
- }
139
- }
122
+ if (!lock) {
123
+ return
140
124
  }
141
- }
142
-
143
- if (!this.isStopping) {
144
- await this.syncWithStorage()
145
- }
146
- })
125
+ await this.runJobs()
126
+ })
127
+ } else {
128
+ await this.runJobs()
129
+ }
147
130
  } catch (error) {
148
- if (error instanceof DOMException && error.name === 'AbortError') {
131
+ if (this.isAbortError(error)) {
149
132
  this.shared.modules.logger.log('Cron: navigator.locks.request was aborted.')
150
133
  } else {
151
134
  console.error('Cron: Error in navigator.locks.request:', error)
@@ -153,13 +136,51 @@ export class Cron {
153
136
  }
154
137
  }
155
138
 
139
+ private async runJobs(): Promise<void> {
140
+ const now = Date.now()
141
+ const storage = await this.getStorageState()
142
+
143
+ for (const [id, job] of this.jobs) {
144
+ if (this.isStopping) {
145
+ break
146
+ }
147
+
148
+ const lastRun = storage.get(id)?.lastRun ?? job.lastRun
149
+ const timeSinceLastRun = now - lastRun
150
+
151
+ if (timeSinceLastRun >= job.interval) {
152
+ try {
153
+ await job.handler()
154
+ if (!this.isStopping) {
155
+ job.lastRun = now
156
+ storage.set(id, { lastRun: now })
157
+ }
158
+ } catch (error) {
159
+ if (this.isAbortError(error)) {
160
+ this.shared.modules.logger.log(`Cron: Job ${id} was aborted.`)
161
+ } else {
162
+ console.error(`Cron job ${id} failed:`, error)
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ if (!this.isStopping) {
169
+ await this.syncWithStorage()
170
+ }
171
+ }
172
+
156
173
  /**
157
174
  * Loads the persisted last run times for jobs from localStorage.
158
175
  * @returns Map of job IDs to their last run times.
159
176
  */
160
177
  private async getStorageState(): Promise<Map<string, { lastRun: number }>> {
161
178
  if (this.isStopping) return new Map()
162
- const state = localStorage.getItem(this.STORAGE_KEY)
179
+ const storage = this.env.storage
180
+ if (!storage) {
181
+ return new Map()
182
+ }
183
+ const state = storage.getItem(this.STORAGE_KEY)
163
184
  return new Map(state ? JSON.parse(state) : [])
164
185
  }
165
186
 
@@ -168,7 +189,19 @@ export class Cron {
168
189
  */
169
190
  private async syncWithStorage() {
170
191
  if (this.isStopping) return
192
+ const storage = this.env.storage
193
+ if (!storage) {
194
+ return
195
+ }
171
196
  const state = Array.from(this.jobs.entries()).map(([id, job]) => [id, { lastRun: job.lastRun }])
172
- localStorage.setItem(this.STORAGE_KEY, JSON.stringify(state))
197
+ storage.setItem(this.STORAGE_KEY, JSON.stringify(state))
198
+ }
199
+
200
+ private isAbortError(error: unknown): boolean {
201
+ const domException = (globalThis as any).DOMException
202
+ if (domException && error instanceof domException) {
203
+ return (error as DOMException).name === 'AbortError'
204
+ }
205
+ return (error as any)?.name === 'AbortError'
173
206
  }
174
207
  }
@@ -1,10 +1,11 @@
1
- import { Hex, Address, Bytes } from 'ox'
1
+ import { Hex, Bytes } from 'ox'
2
2
  import { Handler } from './handler.js'
3
3
  import * as Db from '../../dbs/index.js'
4
4
  import { Signatures } from '../signatures.js'
5
5
  import * as Identity from '@0xsequence/identity-instrument'
6
6
  import { IdentitySigner } from '../../identity/signer.js'
7
7
  import { AuthCodeHandler } from './authcode.js'
8
+ import type { WdkEnv } from '../../env.js'
8
9
 
9
10
  export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
10
11
  constructor(
@@ -16,8 +17,9 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
16
17
  signatures: Signatures,
17
18
  commitments: Db.AuthCommitments,
18
19
  authKeys: Db.AuthKeys,
20
+ env?: WdkEnv,
19
21
  ) {
20
- super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys)
22
+ super(signupKind, issuer, oauthUrl, audience, nitro, signatures, commitments, authKeys, env)
21
23
  }
22
24
 
23
25
  public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string) {
@@ -40,7 +42,7 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
40
42
  isSignUp,
41
43
  })
42
44
 
43
- const searchParams = new URLSearchParams({
45
+ const searchParams = this.serializeQuery({
44
46
  code_challenge: codeChallenge,
45
47
  code_challenge_method: 'S256',
46
48
  client_id: this.audience,
@@ -51,7 +53,7 @@ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
51
53
  state,
52
54
  })
53
55
 
54
- return `${this.oauthUrl}?${searchParams.toString()}`
56
+ return `${this.oauthUrl}?${searchParams}`
55
57
  }
56
58
 
57
59
  public async completeAuth(
@@ -6,6 +6,7 @@ import * as Identity from '@0xsequence/identity-instrument'
6
6
  import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest } from '../types/signature-request.js'
7
7
  import { IdentitySigner } from '../../identity/signer.js'
8
8
  import { IdentityHandler } from './identity.js'
9
+ import type { NavigationLike, WdkEnv } from '../../env.js'
9
10
 
10
11
  export class AuthCodeHandler extends IdentityHandler implements Handler {
11
12
  protected redirectUri: string = ''
@@ -19,8 +20,9 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
19
20
  signatures: Signatures,
20
21
  protected readonly commitments: Db.AuthCommitments,
21
22
  authKeys: Db.AuthKeys,
23
+ env?: WdkEnv,
22
24
  ) {
23
- super(nitro, authKeys, signatures, Identity.IdentityType.OIDC)
25
+ super(nitro, authKeys, signatures, Identity.IdentityType.OIDC, env)
24
26
  }
25
27
 
26
28
  public get kind() {
@@ -45,7 +47,7 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
45
47
  isSignUp,
46
48
  })
47
49
 
48
- const searchParams = new URLSearchParams({
50
+ const searchParams = this.serializeQuery({
49
51
  client_id: this.audience,
50
52
  redirect_uri: this.redirectUri,
51
53
  response_type: 'code',
@@ -53,7 +55,7 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
53
55
  ...(this.signupKind === 'apple' ? {} : { scope: 'openid profile email' }),
54
56
  })
55
57
 
56
- return `${this.oauthUrl}?${searchParams.toString()}`
58
+ return `${this.oauthUrl}?${searchParams}`
57
59
  }
58
60
 
59
61
  public async completeAuth(
@@ -94,10 +96,29 @@ export class AuthCodeHandler extends IdentityHandler implements Handler {
94
96
  status: 'actionable',
95
97
  message: 'request-redirect',
96
98
  handle: async () => {
97
- const url = await this.commitAuth(window.location.pathname, false, request.id, address)
98
- window.location.href = url
99
+ const navigation = this.getNavigation()
100
+ const url = await this.commitAuth(navigation.getPathname(), false, request.id, address)
101
+ navigation.redirect(url)
99
102
  return true
100
103
  },
101
104
  }
102
105
  }
106
+
107
+ protected serializeQuery(params: Record<string, string>): string {
108
+ const searchParamsCtor = this.env.urlSearchParams ?? (globalThis as any).URLSearchParams
109
+ if (searchParamsCtor) {
110
+ return new searchParamsCtor(params).toString()
111
+ }
112
+ return Object.entries(params)
113
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
114
+ .join('&')
115
+ }
116
+
117
+ private getNavigation(): NavigationLike {
118
+ const navigation = this.env.navigation
119
+ if (!navigation) {
120
+ throw new Error('navigation is not available')
121
+ }
122
+ return navigation
123
+ }
103
124
  }
@@ -13,7 +13,7 @@ export class DevicesHandler implements Handler {
13
13
  private readonly devices: Devices,
14
14
  ) {}
15
15
 
16
- onStatusChange(cb: () => void): () => void {
16
+ onStatusChange(_cb: () => void): () => void {
17
17
  return () => {}
18
18
  }
19
19
 
@@ -35,7 +35,7 @@ export class GuardHandler implements Handler {
35
35
  this.onPromptCode = undefined
36
36
  }
37
37
 
38
- onStatusChange(cb: () => void): () => void {
38
+ onStatusChange(_cb: () => void): () => void {
39
39
  return () => {}
40
40
  }
41
41
 
@@ -87,8 +87,9 @@ export class GuardHandler implements Handler {
87
87
  address,
88
88
  handler: this,
89
89
  status: 'ready',
90
- handle: () =>
91
- new Promise(async (resolve, reject) => {
90
+ handle: () => {
91
+ // eslint-disable-next-line no-async-promise-executor
92
+ return new Promise(async (resolve, reject) => {
92
93
  try {
93
94
  const signature = await guard.signEnvelope(request.envelope)
94
95
  await this.signatures.addSignature(request.id, signature)
@@ -106,7 +107,8 @@ export class GuardHandler implements Handler {
106
107
  reject(e)
107
108
  }
108
109
  }
109
- }),
110
+ })
111
+ },
110
112
  }
111
113
  }
112
114
  }
@@ -1,9 +1,10 @@
1
- import { Hex, Bytes } from 'ox'
1
+ import { Hex } from 'ox'
2
2
  import * as Db from '../../dbs/index.js'
3
3
  import * as Identity from '@0xsequence/identity-instrument'
4
4
  import { Signatures } from '../signatures.js'
5
5
  import { BaseSignatureRequest } from '../types/signature-request.js'
6
6
  import { IdentitySigner, toIdentityAuthKey } from '../../identity/signer.js'
7
+ import { resolveWdkEnv, type WdkEnv } from '../../env.js'
7
8
 
8
9
  export const identityTypeToHex = (identityType?: Identity.IdentityType): Hex.Hex => {
9
10
  // Bytes4
@@ -19,12 +20,17 @@ export const identityTypeToHex = (identityType?: Identity.IdentityType): Hex.Hex
19
20
  }
20
21
 
21
22
  export class IdentityHandler {
23
+ protected readonly env: WdkEnv
24
+
22
25
  constructor(
23
26
  private readonly nitro: Identity.IdentityInstrument,
24
27
  private readonly authKeys: Db.AuthKeys,
25
28
  private readonly signatures: Signatures,
26
29
  public readonly identityType: Identity.IdentityType,
27
- ) {}
30
+ env?: WdkEnv,
31
+ ) {
32
+ this.env = resolveWdkEnv(env)
33
+ }
28
34
 
29
35
  public onStatusChange(cb: () => void): () => void {
30
36
  return this.authKeys.addListener(cb)
@@ -37,7 +43,7 @@ export class IdentityHandler {
37
43
  throw new Error('no-auth-key')
38
44
  }
39
45
 
40
- const res = await this.nitro.commitVerifier(toIdentityAuthKey(authKey), challenge)
46
+ const res = await this.nitro.commitVerifier(toIdentityAuthKey(authKey, this.env.crypto), challenge)
41
47
  return res
42
48
  }
43
49
 
@@ -47,7 +53,7 @@ export class IdentityHandler {
47
53
  throw new Error('no-auth-key')
48
54
  }
49
55
 
50
- const res = await this.nitro.completeAuth(toIdentityAuthKey(authKey), challenge)
56
+ const res = await this.nitro.completeAuth(toIdentityAuthKey(authKey, this.env.crypto), challenge)
51
57
 
52
58
  authKey.identitySigner = res.signer.address
53
59
  authKey.expiresAt = new Date(Date.now() + 1000 * 60 * 3) // 3 minutes
@@ -55,7 +61,7 @@ export class IdentityHandler {
55
61
  await this.authKeys.delBySigner(authKey.identitySigner)
56
62
  await this.authKeys.set(authKey)
57
63
 
58
- const signer = new IdentitySigner(this.nitro, authKey)
64
+ const signer = new IdentitySigner(this.nitro, authKey, this.env.crypto)
59
65
  return { signer, email: res.identity.email }
60
66
  }
61
67
 
@@ -72,13 +78,17 @@ export class IdentityHandler {
72
78
  if (!authKey) {
73
79
  return undefined
74
80
  }
75
- return new IdentitySigner(this.nitro, authKey)
81
+ return new IdentitySigner(this.nitro, authKey, this.env.crypto)
76
82
  }
77
83
 
78
84
  private async getAuthKey(signer: string): Promise<Db.AuthKey | undefined> {
79
85
  let authKey = await this.authKeys.getBySigner(signer)
80
86
  if (!signer && !authKey) {
81
- const keyPair = await window.crypto.subtle.generateKey(
87
+ const crypto = this.env.crypto ?? (globalThis as any).crypto
88
+ if (!crypto?.subtle) {
89
+ throw new Error('crypto.subtle is not available')
90
+ }
91
+ const keyPair = await crypto.subtle.generateKey(
82
92
  {
83
93
  name: 'ECDSA',
84
94
  namedCurve: 'P-256',
@@ -86,7 +96,7 @@ export class IdentityHandler {
86
96
  false,
87
97
  ['sign', 'verify'],
88
98
  )
89
- const publicKey = await window.crypto.subtle.exportKey('raw', keyPair.publicKey)
99
+ const publicKey = await crypto.subtle.exportKey('raw', keyPair.publicKey)
90
100
  authKey = {
91
101
  address: Hex.fromBytes(new Uint8Array(publicKey)),
92
102
  identitySigner: '',
@@ -93,8 +93,9 @@ export class MnemonicHandler implements Handler {
93
93
  handler: this,
94
94
  status: 'actionable',
95
95
  message: 'enter-mnemonic',
96
- handle: () =>
97
- new Promise(async (resolve, reject) => {
96
+ handle: () => {
97
+ // eslint-disable-next-line no-async-promise-executor
98
+ return new Promise(async (resolve, reject) => {
98
99
  const respond: RespondFn = async (mnemonic) => {
99
100
  const signer = MnemonicHandler.toSigner(mnemonic)
100
101
  if (!signer) {
@@ -117,7 +118,8 @@ export class MnemonicHandler implements Handler {
117
118
  resolve(true)
118
119
  }
119
120
  await onPromptMnemonic(respond)
120
- }),
121
+ })
122
+ },
121
123
  }
122
124
  }
123
125
  }
@@ -8,6 +8,7 @@ import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest
8
8
  import { Kinds } from '../types/signer.js'
9
9
  import { IdentityHandler } from './identity.js'
10
10
  import { AnswerIncorrectError, ChallengeExpiredError, TooManyAttemptsError } from '../errors.js'
11
+ import type { WdkEnv } from '../../env.js'
11
12
 
12
13
  type RespondFn = (otp: string) => Promise<void>
13
14
 
@@ -18,8 +19,8 @@ export class OtpHandler extends IdentityHandler implements Handler {
18
19
 
19
20
  private onPromptOtp: undefined | PromptOtpHandler
20
21
 
21
- constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys) {
22
- super(nitro, authKeys, signatures, Identity.IdentityType.Email)
22
+ constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys, env?: WdkEnv) {
23
+ super(nitro, authKeys, signatures, Identity.IdentityType.Email, env)
23
24
  }
24
25
 
25
26
  public registerUI(onPromptOtp: PromptOtpHandler) {
@@ -84,7 +85,7 @@ export class OtpHandler extends IdentityHandler implements Handler {
84
85
  try {
85
86
  await this.handleAuth(challenge, onPromptOtp)
86
87
  return true
87
- } catch (e) {
88
+ } catch {
88
89
  return false
89
90
  }
90
91
  },
@@ -95,6 +96,7 @@ export class OtpHandler extends IdentityHandler implements Handler {
95
96
  challenge: Identity.OtpChallenge,
96
97
  onPromptOtp: PromptOtpHandler,
97
98
  ): Promise<{ signer: Signers.Signer & Signers.Witnessable; email: string }> {
99
+ // eslint-disable-next-line no-async-promise-executor
98
100
  return new Promise(async (resolve, reject) => {
99
101
  try {
100
102
  const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
@@ -1,33 +1,35 @@
1
- import { Signers, State } from '@0xsequence/wallet-core'
1
+ import { State } from '@0xsequence/wallet-core'
2
2
  import { Address, Hex } from 'ox'
3
3
  import { Kinds } from '../types/signer.js'
4
4
  import { Signatures } from '../signatures.js'
5
- import { Extensions } from '@0xsequence/wallet-primitives'
5
+ import { Config, Extensions } from '@0xsequence/wallet-primitives'
6
6
  import { Handler } from './handler.js'
7
7
  import { SignerActionable, SignerUnavailable, BaseSignatureRequest } from '../types/index.js'
8
+ import type { PasskeyProvider, PasskeySigner } from '../passkeys-provider.js'
8
9
 
9
10
  export class PasskeysHandler implements Handler {
10
11
  kind = Kinds.LoginPasskey
11
- private readySigners = new Map<string, Signers.Passkey.Passkey>()
12
+ private readySigners = new Map<string, PasskeySigner>()
12
13
 
13
14
  constructor(
14
15
  private readonly signatures: Signatures,
15
16
  private readonly extensions: Pick<Extensions.Extensions, 'passkeys'>,
16
17
  private readonly stateReader: State.Reader,
18
+ private readonly passkeyProvider: PasskeyProvider,
17
19
  ) {}
18
20
 
19
- onStatusChange(cb: () => void): () => void {
21
+ onStatusChange(_cb: () => void): () => void {
20
22
  return () => {}
21
23
  }
22
24
 
23
- public addReadySigner(signer: Signers.Passkey.Passkey) {
25
+ public addReadySigner(signer: PasskeySigner) {
24
26
  // Use credentialId as key to match specific passkey instances
25
27
  this.readySigners.set(signer.credentialId, signer)
26
28
  }
27
29
 
28
- private async loadPasskey(wallet: Address.Address, imageHash: Hex.Hex): Promise<Signers.Passkey.Passkey | undefined> {
30
+ private async loadPasskey(wallet: Address.Address, imageHash: Hex.Hex): Promise<PasskeySigner | undefined> {
29
31
  try {
30
- return await Signers.Passkey.Passkey.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash)
32
+ return await this.passkeyProvider.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash)
31
33
  } catch (e) {
32
34
  console.warn('Failed to load passkey:', e)
33
35
  return undefined
@@ -55,7 +57,7 @@ export class PasskeysHandler implements Handler {
55
57
  }
56
58
 
57
59
  // First check if we have a ready signer that matches the imageHash
58
- let passkey: Signers.Passkey.Passkey | undefined
60
+ let passkey: PasskeySigner | undefined
59
61
 
60
62
  // Look for a ready signer with matching imageHash
61
63
  for (const readySigner of this.readySigners.values()) {
@@ -91,12 +93,10 @@ export class PasskeysHandler implements Handler {
91
93
  message: 'request-interaction-with-passkey',
92
94
  imageHash: imageHash,
93
95
  handle: async () => {
94
- const signature = await passkey.signSapient(
95
- request.envelope.wallet,
96
- request.envelope.chainId,
97
- request.envelope.payload,
98
- imageHash,
96
+ const normalized = Config.normalizeSignerSignature(
97
+ passkey.signSapient(request.envelope.wallet, request.envelope.chainId, request.envelope.payload, imageHash),
99
98
  )
99
+ const signature = await normalized.signature
100
100
  await this.signatures.addSignature(request.id, {
101
101
  address,
102
102
  imageHash,
@@ -76,7 +76,7 @@ export class RecoveryHandler implements Handler {
76
76
  return true
77
77
  },
78
78
  }
79
- } catch (e) {
79
+ } catch {
80
80
  return {
81
81
  address,
82
82
  handler: this,
@@ -3,6 +3,8 @@ export { Network as Networks }
3
3
 
4
4
  export type { ManagerOptions, Databases, Sequence, Modules, Shared } from './manager.js'
5
5
  export { ManagerOptionsDefaults, CreateWalletOptionsDefaults, applyManagerOptionsDefaults, Manager } from './manager.js'
6
+ export { defaultPasskeyProvider } from './passkeys-provider.js'
7
+ export type { PasskeyProvider, PasskeySigner } from './passkeys-provider.js'
6
8
  export { Sessions } from './sessions.js'
7
9
  export { Signatures } from './signatures.js'
8
10
  export type {