@0xsequence/wallet-wdk 0.0.0-20250520201059

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 (173) hide show
  1. package/.env.test +3 -0
  2. package/.turbo/turbo-build.log +5 -0
  3. package/CHANGELOG.md +11 -0
  4. package/LICENSE +202 -0
  5. package/dist/dbs/auth-commitments.d.ts +17 -0
  6. package/dist/dbs/auth-commitments.d.ts.map +1 -0
  7. package/dist/dbs/auth-commitments.js +13 -0
  8. package/dist/dbs/auth-keys.d.ts +19 -0
  9. package/dist/dbs/auth-keys.d.ts.map +1 -0
  10. package/dist/dbs/auth-keys.js +67 -0
  11. package/dist/dbs/generic.d.ts +33 -0
  12. package/dist/dbs/generic.d.ts.map +1 -0
  13. package/dist/dbs/generic.js +170 -0
  14. package/dist/dbs/index.d.ts +12 -0
  15. package/dist/dbs/index.d.ts.map +1 -0
  16. package/dist/dbs/index.js +8 -0
  17. package/dist/dbs/messages.d.ts +6 -0
  18. package/dist/dbs/messages.d.ts.map +1 -0
  19. package/dist/dbs/messages.js +13 -0
  20. package/dist/dbs/recovery.d.ts +6 -0
  21. package/dist/dbs/recovery.d.ts.map +1 -0
  22. package/dist/dbs/recovery.js +13 -0
  23. package/dist/dbs/signatures.d.ts +6 -0
  24. package/dist/dbs/signatures.d.ts.map +1 -0
  25. package/dist/dbs/signatures.js +13 -0
  26. package/dist/dbs/transactions.d.ts +6 -0
  27. package/dist/dbs/transactions.d.ts.map +1 -0
  28. package/dist/dbs/transactions.js +13 -0
  29. package/dist/dbs/wallets.d.ts +6 -0
  30. package/dist/dbs/wallets.d.ts.map +1 -0
  31. package/dist/dbs/wallets.js +13 -0
  32. package/dist/identity/signer.d.ts +17 -0
  33. package/dist/identity/signer.d.ts.map +1 -0
  34. package/dist/identity/signer.js +58 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +2 -0
  38. package/dist/sequence/cron.d.ts +19 -0
  39. package/dist/sequence/cron.d.ts.map +1 -0
  40. package/dist/sequence/cron.js +118 -0
  41. package/dist/sequence/devices.d.ts +14 -0
  42. package/dist/sequence/devices.d.ts.map +1 -0
  43. package/dist/sequence/devices.js +43 -0
  44. package/dist/sequence/handlers/authcode-pkce.d.ts +14 -0
  45. package/dist/sequence/handlers/authcode-pkce.d.ts.map +1 -0
  46. package/dist/sequence/handlers/authcode-pkce.js +48 -0
  47. package/dist/sequence/handlers/authcode.d.ts +25 -0
  48. package/dist/sequence/handlers/authcode.d.ts.map +1 -0
  49. package/dist/sequence/handlers/authcode.js +91 -0
  50. package/dist/sequence/handlers/devices.d.ts +14 -0
  51. package/dist/sequence/handlers/devices.d.ts.map +1 -0
  52. package/dist/sequence/handlers/devices.js +39 -0
  53. package/dist/sequence/handlers/handler.d.ts +8 -0
  54. package/dist/sequence/handlers/handler.d.ts.map +1 -0
  55. package/dist/sequence/handlers/handler.js +1 -0
  56. package/dist/sequence/handlers/identity.d.ts +21 -0
  57. package/dist/sequence/handlers/identity.d.ts.map +1 -0
  58. package/dist/sequence/handlers/identity.js +86 -0
  59. package/dist/sequence/handlers/index.d.ts +7 -0
  60. package/dist/sequence/handlers/index.d.ts.map +1 -0
  61. package/dist/sequence/handlers/index.js +5 -0
  62. package/dist/sequence/handlers/mnemonic.d.ts +19 -0
  63. package/dist/sequence/handlers/mnemonic.d.ts.map +1 -0
  64. package/dist/sequence/handlers/mnemonic.js +67 -0
  65. package/dist/sequence/handlers/otp.d.ts +20 -0
  66. package/dist/sequence/handlers/otp.d.ts.map +1 -0
  67. package/dist/sequence/handlers/otp.js +83 -0
  68. package/dist/sequence/handlers/passkeys.d.ts +17 -0
  69. package/dist/sequence/handlers/passkeys.d.ts.map +1 -0
  70. package/dist/sequence/handlers/passkeys.js +63 -0
  71. package/dist/sequence/handlers/recovery.d.ts +15 -0
  72. package/dist/sequence/handlers/recovery.d.ts.map +1 -0
  73. package/dist/sequence/handlers/recovery.js +72 -0
  74. package/dist/sequence/index.d.ts +12 -0
  75. package/dist/sequence/index.d.ts.map +1 -0
  76. package/dist/sequence/index.js +9 -0
  77. package/dist/sequence/logger.d.ts +7 -0
  78. package/dist/sequence/logger.d.ts.map +1 -0
  79. package/dist/sequence/logger.js +11 -0
  80. package/dist/sequence/manager.d.ts +287 -0
  81. package/dist/sequence/manager.d.ts.map +1 -0
  82. package/dist/sequence/manager.js +356 -0
  83. package/dist/sequence/messages.d.ts +18 -0
  84. package/dist/sequence/messages.d.ts.map +1 -0
  85. package/dist/sequence/messages.js +115 -0
  86. package/dist/sequence/recovery.d.ts +30 -0
  87. package/dist/sequence/recovery.d.ts.map +1 -0
  88. package/dist/sequence/recovery.js +314 -0
  89. package/dist/sequence/sessions.d.ts +26 -0
  90. package/dist/sequence/sessions.d.ts.map +1 -0
  91. package/dist/sequence/sessions.js +169 -0
  92. package/dist/sequence/signatures.d.ts +21 -0
  93. package/dist/sequence/signatures.d.ts.map +1 -0
  94. package/dist/sequence/signatures.js +192 -0
  95. package/dist/sequence/signers.d.ts +14 -0
  96. package/dist/sequence/signers.d.ts.map +1 -0
  97. package/dist/sequence/signers.js +74 -0
  98. package/dist/sequence/transactions.d.ts +26 -0
  99. package/dist/sequence/transactions.d.ts.map +1 -0
  100. package/dist/sequence/transactions.js +201 -0
  101. package/dist/sequence/types/index.d.ts +9 -0
  102. package/dist/sequence/types/index.d.ts.map +1 -0
  103. package/dist/sequence/types/index.js +2 -0
  104. package/dist/sequence/types/message-request.d.ts +23 -0
  105. package/dist/sequence/types/message-request.d.ts.map +1 -0
  106. package/dist/sequence/types/message-request.js +1 -0
  107. package/dist/sequence/types/recovery.d.ts +15 -0
  108. package/dist/sequence/types/recovery.d.ts.map +1 -0
  109. package/dist/sequence/types/recovery.js +1 -0
  110. package/dist/sequence/types/signature-request.d.ts +76 -0
  111. package/dist/sequence/types/signature-request.d.ts.map +1 -0
  112. package/dist/sequence/types/signature-request.js +11 -0
  113. package/dist/sequence/types/signer.d.ts +28 -0
  114. package/dist/sequence/types/signer.d.ts.map +1 -0
  115. package/dist/sequence/types/signer.js +10 -0
  116. package/dist/sequence/types/transaction-request.d.ts +41 -0
  117. package/dist/sequence/types/transaction-request.d.ts.map +1 -0
  118. package/dist/sequence/types/transaction-request.js +1 -0
  119. package/dist/sequence/types/wallet.d.ts +21 -0
  120. package/dist/sequence/types/wallet.d.ts.map +1 -0
  121. package/dist/sequence/types/wallet.js +1 -0
  122. package/dist/sequence/wallets.d.ts +121 -0
  123. package/dist/sequence/wallets.d.ts.map +1 -0
  124. package/dist/sequence/wallets.js +632 -0
  125. package/package.json +40 -0
  126. package/src/dbs/auth-commitments.ts +26 -0
  127. package/src/dbs/auth-keys.ts +85 -0
  128. package/src/dbs/generic.ts +194 -0
  129. package/src/dbs/index.ts +13 -0
  130. package/src/dbs/messages.ts +16 -0
  131. package/src/dbs/recovery.ts +15 -0
  132. package/src/dbs/signatures.ts +15 -0
  133. package/src/dbs/transactions.ts +16 -0
  134. package/src/dbs/wallets.ts +16 -0
  135. package/src/identity/signer.ts +78 -0
  136. package/src/index.ts +2 -0
  137. package/src/sequence/cron.ts +134 -0
  138. package/src/sequence/devices.ts +53 -0
  139. package/src/sequence/handlers/authcode-pkce.ts +70 -0
  140. package/src/sequence/handlers/authcode.ts +116 -0
  141. package/src/sequence/handlers/devices.ts +53 -0
  142. package/src/sequence/handlers/handler.ts +14 -0
  143. package/src/sequence/handlers/identity.ts +101 -0
  144. package/src/sequence/handlers/index.ts +6 -0
  145. package/src/sequence/handlers/mnemonic.ts +88 -0
  146. package/src/sequence/handlers/otp.ts +107 -0
  147. package/src/sequence/handlers/passkeys.ts +84 -0
  148. package/src/sequence/handlers/recovery.ts +88 -0
  149. package/src/sequence/index.ts +25 -0
  150. package/src/sequence/logger.ts +11 -0
  151. package/src/sequence/manager.ts +634 -0
  152. package/src/sequence/messages.ts +146 -0
  153. package/src/sequence/recovery.ts +429 -0
  154. package/src/sequence/sessions.ts +238 -0
  155. package/src/sequence/signatures.ts +263 -0
  156. package/src/sequence/signers.ts +88 -0
  157. package/src/sequence/transactions.ts +281 -0
  158. package/src/sequence/types/index.ts +27 -0
  159. package/src/sequence/types/message-request.ts +26 -0
  160. package/src/sequence/types/recovery.ts +15 -0
  161. package/src/sequence/types/signature-request.ts +89 -0
  162. package/src/sequence/types/signer.ts +32 -0
  163. package/src/sequence/types/transaction-request.ts +47 -0
  164. package/src/sequence/types/wallet.ts +24 -0
  165. package/src/sequence/wallets.ts +853 -0
  166. package/test/constants.ts +62 -0
  167. package/test/recovery.test.ts +211 -0
  168. package/test/sessions.test.ts +324 -0
  169. package/test/setup.ts +63 -0
  170. package/test/transactions.test.ts +464 -0
  171. package/test/wallets.test.ts +381 -0
  172. package/tsconfig.json +10 -0
  173. package/vitest.config.ts +11 -0
@@ -0,0 +1,70 @@
1
+ import { Hex, Address, Bytes } from 'ox'
2
+ import { Handler } from './handler.js'
3
+ import * as Db from '../../dbs/index.js'
4
+ import { Signatures } from '../signatures.js'
5
+ import * as Identity from '@0xsequence/identity-instrument'
6
+ import { IdentitySigner } from '../../identity/signer.js'
7
+ import { AuthCodeHandler } from './authcode.js'
8
+
9
+ export class AuthCodePkceHandler extends AuthCodeHandler implements Handler {
10
+ constructor(
11
+ signupKind: 'google-pkce',
12
+ issuer: string,
13
+ audience: string,
14
+ nitro: Identity.IdentityInstrument,
15
+ signatures: Signatures,
16
+ commitments: Db.AuthCommitments,
17
+ authKeys: Db.AuthKeys,
18
+ ) {
19
+ super(signupKind, issuer, audience, nitro, signatures, commitments, authKeys)
20
+ }
21
+
22
+ public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string) {
23
+ let challenge = new Identity.AuthCodePkceChallenge(this.issuer, this.audience, this.redirectUri)
24
+ if (signer) {
25
+ challenge = challenge.withSigner(signer)
26
+ }
27
+ const { verifier, loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
28
+ if (!state) {
29
+ state = Hex.fromBytes(Bytes.random(32))
30
+ }
31
+
32
+ await this.commitments.set({
33
+ id: state,
34
+ kind: this.signupKind,
35
+ verifier,
36
+ challenge: codeChallenge,
37
+ target,
38
+ metadata: {},
39
+ isSignUp,
40
+ })
41
+
42
+ const searchParams = new URLSearchParams({
43
+ code_challenge: codeChallenge,
44
+ code_challenge_method: 'S256',
45
+ client_id: this.audience,
46
+ redirect_uri: this.redirectUri,
47
+ login_hint: loginHint,
48
+ response_type: 'code',
49
+ scope: 'openid profile email',
50
+ state,
51
+ })
52
+
53
+ const oauthUrl = this.oauthUrl()
54
+ return `${oauthUrl}?${searchParams.toString()}`
55
+ }
56
+
57
+ public async completeAuth(
58
+ commitment: Db.AuthCommitment,
59
+ code: string,
60
+ ): Promise<[IdentitySigner, { [key: string]: string }]> {
61
+ const challenge = new Identity.AuthCodePkceChallenge('', '', '')
62
+ if (!commitment.verifier) {
63
+ throw new Error('Missing verifier in commitment')
64
+ }
65
+ const signer = await this.nitroCompleteAuth(challenge.withAnswer(commitment.verifier, code))
66
+ await this.commitments.del(commitment.id)
67
+
68
+ return [signer, commitment.metadata]
69
+ }
70
+ }
@@ -0,0 +1,116 @@
1
+ import { Hex, Address, Bytes } from 'ox'
2
+ import { Handler } from './handler.js'
3
+ import * as Db from '../../dbs/index.js'
4
+ import { Signatures } from '../signatures.js'
5
+ import * as Identity from '@0xsequence/identity-instrument'
6
+ import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest } from '../types/signature-request.js'
7
+ import { IdentitySigner } from '../../identity/signer.js'
8
+ import { IdentityHandler } from './identity.js'
9
+
10
+ export class AuthCodeHandler extends IdentityHandler implements Handler {
11
+ protected redirectUri: string = ''
12
+
13
+ constructor(
14
+ public readonly signupKind: 'apple' | 'google-pkce',
15
+ public readonly issuer: string,
16
+ public readonly audience: string,
17
+ nitro: Identity.IdentityInstrument,
18
+ signatures: Signatures,
19
+ protected readonly commitments: Db.AuthCommitments,
20
+ authKeys: Db.AuthKeys,
21
+ ) {
22
+ super(nitro, authKeys, signatures, Identity.IdentityType.OIDC)
23
+ }
24
+
25
+ public get kind() {
26
+ return 'login-' + this.signupKind
27
+ }
28
+
29
+ public setRedirectUri(redirectUri: string) {
30
+ this.redirectUri = redirectUri
31
+ }
32
+
33
+ public async commitAuth(target: string, isSignUp: boolean, state?: string, signer?: string) {
34
+ if (!state) {
35
+ state = Hex.fromBytes(Bytes.random(32))
36
+ }
37
+
38
+ await this.commitments.set({
39
+ id: state,
40
+ kind: this.signupKind,
41
+ signer,
42
+ target,
43
+ metadata: {},
44
+ isSignUp,
45
+ })
46
+
47
+ const searchParams = new URLSearchParams({
48
+ client_id: this.audience,
49
+ redirect_uri: this.redirectUri,
50
+ response_type: 'code',
51
+ scope: 'openid',
52
+ state,
53
+ })
54
+
55
+ const oauthUrl = this.oauthUrl()
56
+ return `${oauthUrl}?${searchParams.toString()}`
57
+ }
58
+
59
+ public async completeAuth(
60
+ commitment: Db.AuthCommitment,
61
+ code: string,
62
+ ): Promise<[IdentitySigner, { [key: string]: string }]> {
63
+ let challenge = new Identity.AuthCodeChallenge(this.issuer, this.audience, this.redirectUri, code)
64
+ if (commitment.signer) {
65
+ challenge = challenge.withSigner(commitment.signer)
66
+ }
67
+ await this.nitroCommitVerifier(challenge)
68
+ const signer = await this.nitroCompleteAuth(challenge)
69
+
70
+ return [signer, {}]
71
+ }
72
+
73
+ async status(
74
+ address: Address.Address,
75
+ _imageHash: Hex.Hex | undefined,
76
+ request: BaseSignatureRequest,
77
+ ): Promise<SignerUnavailable | SignerReady | SignerActionable> {
78
+ // Normalize address
79
+ const normalizedAddress = Address.checksum(address)
80
+ const signer = await this.getAuthKeySigner(normalizedAddress)
81
+ if (signer) {
82
+ return {
83
+ address: normalizedAddress,
84
+ handler: this,
85
+ status: 'ready',
86
+ handle: async () => {
87
+ await this.sign(signer, request)
88
+ return true
89
+ },
90
+ }
91
+ }
92
+
93
+ return {
94
+ address: normalizedAddress,
95
+ handler: this,
96
+ status: 'actionable',
97
+ message: 'request-redirect',
98
+ handle: async () => {
99
+ const url = await this.commitAuth(window.location.pathname, false, request.id, normalizedAddress)
100
+ window.location.href = url
101
+ return true
102
+ },
103
+ }
104
+ }
105
+
106
+ protected oauthUrl() {
107
+ switch (this.issuer) {
108
+ case 'https://accounts.google.com':
109
+ return 'https://accounts.google.com/o/oauth2/v2/auth'
110
+ case 'https://appleid.apple.com':
111
+ return 'https://appleid.apple.com/auth/authorize'
112
+ default:
113
+ throw new Error('unsupported-issuer')
114
+ }
115
+ }
116
+ }
@@ -0,0 +1,53 @@
1
+ import { Kinds } from '../types/signer.js'
2
+ import { Signatures } from '../signatures.js'
3
+ import { Address, Hex } from 'ox'
4
+ import { Devices } from '../devices.js'
5
+ import { Handler } from './handler.js'
6
+ import { SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/index.js'
7
+
8
+ export class DevicesHandler implements Handler {
9
+ kind = Kinds.LocalDevice
10
+
11
+ constructor(
12
+ private readonly signatures: Signatures,
13
+ private readonly devices: Devices,
14
+ ) {}
15
+
16
+ onStatusChange(cb: () => void): () => void {
17
+ return () => {}
18
+ }
19
+
20
+ async status(
21
+ address: Address.Address,
22
+ _imageHash: Hex.Hex | undefined,
23
+ request: BaseSignatureRequest,
24
+ ): Promise<SignerUnavailable | SignerReady> {
25
+ const signer = await this.devices.get(address)
26
+ if (!signer) {
27
+ const status: SignerUnavailable = {
28
+ address,
29
+ handler: this,
30
+ reason: 'not-local-key',
31
+ status: 'unavailable',
32
+ }
33
+ return status
34
+ }
35
+
36
+ const status: SignerReady = {
37
+ address,
38
+ handler: this,
39
+ status: 'ready',
40
+ handle: async () => {
41
+ const signature = await signer.sign(request.envelope.wallet, request.envelope.chainId, request.envelope.payload)
42
+
43
+ await this.signatures.addSignature(request.id, {
44
+ address,
45
+ signature,
46
+ })
47
+
48
+ return true
49
+ },
50
+ }
51
+ return status
52
+ }
53
+ }
@@ -0,0 +1,14 @@
1
+ import { Address, Hex } from 'ox'
2
+ import { SignerActionable, SignerReady, SignerUnavailable, BaseSignatureRequest } from '../types/index.js'
3
+
4
+ export interface Handler {
5
+ kind: string
6
+
7
+ onStatusChange(cb: () => void): () => void
8
+
9
+ status(
10
+ address: Address.Address,
11
+ imageHash: Hex.Hex | undefined,
12
+ request: BaseSignatureRequest,
13
+ ): Promise<SignerUnavailable | SignerReady | SignerActionable>
14
+ }
@@ -0,0 +1,101 @@
1
+ import { Hex, Bytes } from 'ox'
2
+ import * as Db from '../../dbs/index.js'
3
+ import * as Identity from '@0xsequence/identity-instrument'
4
+ import { Signatures } from '../signatures.js'
5
+ import { BaseSignatureRequest } from '../types/signature-request.js'
6
+ import { IdentitySigner, toIdentityAuthKey } from '../../identity/signer.js'
7
+
8
+ export const identityTypeToHex = (identityType?: Identity.IdentityType): Hex.Hex => {
9
+ // Bytes4
10
+ switch (identityType) {
11
+ case Identity.IdentityType.Guest:
12
+ return '0x00000000'
13
+ case Identity.IdentityType.Email:
14
+ return '0x00000001'
15
+ case Identity.IdentityType.OIDC:
16
+ return '0x00000002'
17
+ default:
18
+ // Unknown identity type
19
+ return '0xffffffff'
20
+ }
21
+ }
22
+
23
+ export class IdentityHandler {
24
+ constructor(
25
+ private readonly nitro: Identity.IdentityInstrument,
26
+ private readonly authKeys: Db.AuthKeys,
27
+ private readonly signatures: Signatures,
28
+ public readonly identityType: Identity.IdentityType,
29
+ ) {}
30
+
31
+ public onStatusChange(cb: () => void): () => void {
32
+ return this.authKeys.addListener(cb)
33
+ }
34
+
35
+ protected async nitroCommitVerifier(challenge: Identity.Challenge) {
36
+ await this.authKeys.delBySigner('')
37
+ const authKey = await this.getAuthKey('')
38
+ if (!authKey) {
39
+ throw new Error('no-auth-key')
40
+ }
41
+
42
+ const res = await this.nitro.commitVerifier(toIdentityAuthKey(authKey), challenge)
43
+ return res
44
+ }
45
+
46
+ protected async nitroCompleteAuth(challenge: Identity.Challenge) {
47
+ const authKey = await this.getAuthKey('')
48
+ if (!authKey) {
49
+ throw new Error('no-auth-key')
50
+ }
51
+
52
+ const res = await this.nitro.completeAuth(toIdentityAuthKey(authKey), challenge)
53
+
54
+ authKey.identitySigner = res.signer
55
+ authKey.expiresAt = new Date(Date.now() + 1000 * 60 * 3) // 3 minutes
56
+ await this.authKeys.delBySigner('')
57
+ await this.authKeys.set(authKey)
58
+
59
+ const signer = new IdentitySigner(this.nitro, authKey)
60
+ return signer
61
+ }
62
+
63
+ protected async sign(signer: IdentitySigner, request: BaseSignatureRequest) {
64
+ const signature = await signer.sign(request.envelope.wallet, request.envelope.chainId, request.envelope.payload)
65
+ await this.signatures.addSignature(request.id, {
66
+ address: signer.address,
67
+ signature,
68
+ })
69
+ }
70
+
71
+ protected async getAuthKeySigner(address: string): Promise<IdentitySigner | undefined> {
72
+ const authKey = await this.getAuthKey(address)
73
+ if (!authKey) {
74
+ return undefined
75
+ }
76
+ return new IdentitySigner(this.nitro, authKey)
77
+ }
78
+
79
+ private async getAuthKey(signer: string): Promise<Db.AuthKey | undefined> {
80
+ let authKey = await this.authKeys.getBySigner(signer)
81
+ if (!signer && !authKey) {
82
+ const keyPair = await window.crypto.subtle.generateKey(
83
+ {
84
+ name: 'ECDSA',
85
+ namedCurve: 'P-256',
86
+ },
87
+ false,
88
+ ['sign', 'verify'],
89
+ )
90
+ const publicKey = await window.crypto.subtle.exportKey('raw', keyPair.publicKey)
91
+ authKey = {
92
+ address: Hex.fromBytes(new Uint8Array(publicKey)),
93
+ identitySigner: '',
94
+ expiresAt: new Date(Date.now() + 1000 * 60 * 60), // 1 hour
95
+ privateKey: keyPair.privateKey,
96
+ }
97
+ await this.authKeys.set(authKey)
98
+ }
99
+ return authKey
100
+ }
101
+ }
@@ -0,0 +1,6 @@
1
+ export type { Handler } from './handler.js'
2
+ export { DevicesHandler } from './devices.js'
3
+ export { PasskeysHandler } from './passkeys.js'
4
+ export { OtpHandler } from './otp.js'
5
+ export { AuthCodePkceHandler } from './authcode-pkce.js'
6
+ export { MnemonicHandler } from './mnemonic.js'
@@ -0,0 +1,88 @@
1
+ import { Signers } from '@0xsequence/wallet-core'
2
+ import { Address, Hex, Mnemonic } from 'ox'
3
+ import { Handler } from './handler.js'
4
+ import { Signatures } from '../signatures.js'
5
+ import { Kinds } from '../types/signer.js'
6
+ import { SignerReady, SignerUnavailable, BaseSignatureRequest, SignerActionable } from '../types/index.js'
7
+
8
+ type RespondFn = (mnemonic: string) => Promise<void>
9
+
10
+ export class MnemonicHandler implements Handler {
11
+ kind = Kinds.LoginMnemonic
12
+
13
+ private onPromptMnemonic: undefined | ((respond: RespondFn) => Promise<void>)
14
+
15
+ constructor(private readonly signatures: Signatures) {}
16
+
17
+ public registerUI(onPromptMnemonic: (respond: RespondFn) => Promise<void>) {
18
+ this.onPromptMnemonic = onPromptMnemonic
19
+ return () => {
20
+ this.onPromptMnemonic = undefined
21
+ }
22
+ }
23
+
24
+ public unregisterUI() {
25
+ this.onPromptMnemonic = undefined
26
+ }
27
+
28
+ onStatusChange(_cb: () => void): () => void {
29
+ return () => {}
30
+ }
31
+
32
+ public static toSigner(mnemonic: string): Signers.Pk.Pk | undefined {
33
+ try {
34
+ const pk = Mnemonic.toPrivateKey(mnemonic)
35
+ return new Signers.Pk.Pk(Hex.from(pk))
36
+ } catch {
37
+ return undefined
38
+ }
39
+ }
40
+
41
+ async status(
42
+ address: Address.Address,
43
+ _imageHash: Hex.Hex | undefined,
44
+ request: BaseSignatureRequest,
45
+ ): Promise<SignerUnavailable | SignerActionable> {
46
+ const onPromptMnemonic = this.onPromptMnemonic
47
+ if (!onPromptMnemonic) {
48
+ return {
49
+ address,
50
+ handler: this,
51
+ reason: 'ui-not-registered',
52
+ status: 'unavailable',
53
+ }
54
+ }
55
+
56
+ return {
57
+ address,
58
+ handler: this,
59
+ status: 'actionable',
60
+ message: 'enter-mnemonic',
61
+ handle: () =>
62
+ new Promise(async (resolve, reject) => {
63
+ const respond = async (mnemonic: string) => {
64
+ const signer = MnemonicHandler.toSigner(mnemonic)
65
+ if (!signer) {
66
+ return reject('invalid-mnemonic')
67
+ }
68
+
69
+ if (signer.address !== address) {
70
+ return reject('wrong-mnemonic')
71
+ }
72
+
73
+ const signature = await signer.sign(
74
+ request.envelope.wallet,
75
+ request.envelope.chainId,
76
+ request.envelope.payload,
77
+ )
78
+ await this.signatures.addSignature(request.id, {
79
+ address,
80
+ signature,
81
+ })
82
+ resolve(true)
83
+ }
84
+ await onPromptMnemonic(respond)
85
+ }),
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,107 @@
1
+ import { Hex, Address } from 'ox'
2
+ import { Signers } from '@0xsequence/wallet-core'
3
+ import * as Identity from '@0xsequence/identity-instrument'
4
+ import { Handler } from './handler.js'
5
+ import * as Db from '../../dbs/index.js'
6
+ import { Signatures } from '../signatures.js'
7
+ import { SignerUnavailable, SignerReady, SignerActionable, BaseSignatureRequest } from '../types/signature-request.js'
8
+ import { Kinds } from '../types/signer.js'
9
+ import { IdentityHandler } from './identity.js'
10
+
11
+ type RespondFn = (otp: string) => Promise<void>
12
+
13
+ export class OtpHandler extends IdentityHandler implements Handler {
14
+ kind = Kinds.LoginEmailOtp
15
+
16
+ private onPromptOtp: undefined | ((recipient: string, respond: RespondFn) => Promise<void>)
17
+
18
+ constructor(nitro: Identity.IdentityInstrument, signatures: Signatures, authKeys: Db.AuthKeys) {
19
+ super(nitro, authKeys, signatures, Identity.IdentityType.Email)
20
+ }
21
+
22
+ public registerUI(onPromptOtp: (recipient: string, respond: RespondFn) => Promise<void>) {
23
+ this.onPromptOtp = onPromptOtp
24
+ return () => {
25
+ this.onPromptOtp = undefined
26
+ }
27
+ }
28
+
29
+ public unregisterUI() {
30
+ this.onPromptOtp = undefined
31
+ }
32
+
33
+ public async getSigner(email: string): Promise<Signers.Signer & Signers.Witnessable> {
34
+ const onPromptOtp = this.onPromptOtp
35
+ if (!onPromptOtp) {
36
+ throw new Error('otp-handler-ui-not-registered')
37
+ }
38
+
39
+ const challenge = Identity.OtpChallenge.fromRecipient(this.identityType, email)
40
+ const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
41
+
42
+ return new Promise(async (resolve, reject) => {
43
+ const respond = async (otp: string) => {
44
+ try {
45
+ const signer = await this.nitroCompleteAuth(challenge.withAnswer(codeChallenge, otp))
46
+ resolve(signer)
47
+ } catch (e) {
48
+ reject(e)
49
+ }
50
+ }
51
+ await onPromptOtp(loginHint, respond)
52
+ })
53
+ }
54
+
55
+ async status(
56
+ address: Address.Address,
57
+ _imageHash: Hex.Hex | undefined,
58
+ request: BaseSignatureRequest,
59
+ ): Promise<SignerUnavailable | SignerReady | SignerActionable> {
60
+ const onPromptOtp = this.onPromptOtp
61
+ if (!onPromptOtp) {
62
+ return {
63
+ address,
64
+ handler: this,
65
+ reason: 'ui-not-registered',
66
+ status: 'unavailable',
67
+ }
68
+ }
69
+
70
+ const signer = await this.getAuthKeySigner(address)
71
+ if (signer) {
72
+ return {
73
+ address,
74
+ handler: this,
75
+ status: 'ready',
76
+ handle: async () => {
77
+ await this.sign(signer, request)
78
+ return true
79
+ },
80
+ }
81
+ }
82
+
83
+ return {
84
+ address,
85
+ handler: this,
86
+ status: 'actionable',
87
+ message: 'request-otp',
88
+ handle: () =>
89
+ new Promise(async (resolve, reject) => {
90
+ const challenge = Identity.OtpChallenge.fromSigner(this.identityType, address)
91
+ const { loginHint, challenge: codeChallenge } = await this.nitroCommitVerifier(challenge)
92
+
93
+ const respond = async (otp: string) => {
94
+ try {
95
+ await this.nitroCompleteAuth(challenge.withAnswer(codeChallenge, otp))
96
+ resolve(true)
97
+ } catch (e) {
98
+ resolve(false)
99
+ throw e
100
+ }
101
+ }
102
+
103
+ await onPromptOtp(loginHint, respond)
104
+ }),
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,84 @@
1
+ import { Signers, State } from '@0xsequence/wallet-core'
2
+ import { Address, Hex } from 'ox'
3
+ import { Kinds } from '../types/signer.js'
4
+ import { Signatures } from '../signatures.js'
5
+ import { Extensions } from '@0xsequence/wallet-primitives'
6
+ import { Handler } from './handler.js'
7
+ import { SignerActionable, SignerUnavailable, BaseSignatureRequest } from '../types/index.js'
8
+
9
+ export class PasskeysHandler implements Handler {
10
+ kind = Kinds.LoginPasskey
11
+
12
+ constructor(
13
+ private readonly signatures: Signatures,
14
+ private readonly extensions: Pick<Extensions.Extensions, 'passkeys'>,
15
+ private readonly stateReader: State.Reader,
16
+ ) {}
17
+
18
+ onStatusChange(cb: () => void): () => void {
19
+ return () => {}
20
+ }
21
+
22
+ private async loadPasskey(wallet: Address.Address, imageHash: Hex.Hex): Promise<Signers.Passkey.Passkey | undefined> {
23
+ try {
24
+ return await Signers.Passkey.Passkey.loadFromWitness(this.stateReader, this.extensions, wallet, imageHash)
25
+ } catch (e) {
26
+ console.warn('Failed to load passkey:', e)
27
+ return undefined
28
+ }
29
+ }
30
+
31
+ async status(
32
+ address: Address.Address,
33
+ imageHash: Hex.Hex | undefined,
34
+ request: BaseSignatureRequest,
35
+ ): Promise<SignerActionable | SignerUnavailable> {
36
+ const base = { address, imageHash, handler: this }
37
+ if (address !== this.extensions.passkeys) {
38
+ console.warn(
39
+ 'PasskeySigner: status address does not match passkey module address',
40
+ address,
41
+ this.extensions.passkeys,
42
+ )
43
+ const status: SignerUnavailable = {
44
+ ...base,
45
+ status: 'unavailable',
46
+ reason: 'unknown-error',
47
+ }
48
+ return status
49
+ }
50
+
51
+ const passkey = imageHash && (await this.loadPasskey(request.envelope.wallet, imageHash))
52
+ if (!passkey) {
53
+ console.warn('PasskeySigner: status failed to load passkey', address, imageHash)
54
+ const status: SignerUnavailable = {
55
+ ...base,
56
+ status: 'unavailable',
57
+ reason: 'unknown-error',
58
+ }
59
+ return status
60
+ }
61
+
62
+ const status: SignerActionable = {
63
+ ...base,
64
+ status: 'actionable',
65
+ message: 'request-interaction-with-passkey',
66
+ imageHash: imageHash,
67
+ handle: async () => {
68
+ const signature = await passkey.signSapient(
69
+ request.envelope.wallet,
70
+ request.envelope.chainId,
71
+ request.envelope.payload,
72
+ imageHash,
73
+ )
74
+ await this.signatures.addSignature(request.id, {
75
+ address,
76
+ imageHash,
77
+ signature,
78
+ })
79
+ return true
80
+ },
81
+ }
82
+ return status
83
+ }
84
+ }