@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,632 @@
1
+ import { Wallet as CoreWallet, Envelope, Signers, State } from '@0xsequence/wallet-core';
2
+ import { Config, GenericTree, Payload, SessionConfig } from '@0xsequence/wallet-primitives';
3
+ import { Address, Provider, RpcTransport } from 'ox';
4
+ import { MnemonicHandler } from './handlers/mnemonic.js';
5
+ import { Kinds } from './types/signer.js';
6
+ export function isLoginToWalletArgs(args) {
7
+ return 'wallet' in args;
8
+ }
9
+ export function isLoginToMnemonicArgs(args) {
10
+ return 'kind' in args && args.kind === 'mnemonic';
11
+ }
12
+ export function isLoginToPasskeyArgs(args) {
13
+ return 'kind' in args && args.kind === 'passkey';
14
+ }
15
+ export function isAuthCodeArgs(args) {
16
+ return 'kind' in args && (args.kind === 'google-pkce' || args.kind === 'apple');
17
+ }
18
+ function buildCappedTree(members) {
19
+ const loginMemberWeight = 1n;
20
+ if (members.length === 0) {
21
+ // We need to maintain the general structure of the tree, so we can't have an empty node here
22
+ // instead, we add a dummy signer with weight 0
23
+ return {
24
+ type: 'signer',
25
+ address: '0x0000000000000000000000000000000000000000',
26
+ weight: 0n,
27
+ };
28
+ }
29
+ if (members.length === 1) {
30
+ if (members[0].imageHash) {
31
+ return {
32
+ type: 'sapient-signer',
33
+ address: members[0].address,
34
+ imageHash: members[0].imageHash,
35
+ weight: loginMemberWeight,
36
+ };
37
+ }
38
+ else {
39
+ return {
40
+ type: 'signer',
41
+ address: members[0].address,
42
+ weight: loginMemberWeight,
43
+ };
44
+ }
45
+ }
46
+ return {
47
+ type: 'nested',
48
+ weight: loginMemberWeight,
49
+ threshold: 1n,
50
+ tree: Config.flatLeavesToTopology(members.map((member) => member.imageHash
51
+ ? {
52
+ type: 'sapient-signer',
53
+ address: member.address,
54
+ imageHash: member.imageHash,
55
+ weight: 1n,
56
+ }
57
+ : {
58
+ type: 'signer',
59
+ address: member.address,
60
+ weight: 1n,
61
+ })),
62
+ };
63
+ }
64
+ function buildCappedTreeFromTopology(weight, topology) {
65
+ // We may optimize this for some topology types
66
+ // but it is not worth it, because the topology
67
+ // that we will use for prod won't be optimizable
68
+ return {
69
+ type: 'nested',
70
+ weight: weight,
71
+ threshold: weight,
72
+ tree: topology,
73
+ };
74
+ }
75
+ function toConfig(checkpoint, loginTopology, devicesTopology, modules, guardTopology) {
76
+ if (!guardTopology) {
77
+ return {
78
+ checkpoint: checkpoint,
79
+ threshold: 1n,
80
+ topology: [[loginTopology, devicesTopology], toModulesTopology(modules)],
81
+ };
82
+ }
83
+ else {
84
+ return {
85
+ checkpoint: checkpoint,
86
+ threshold: 2n,
87
+ topology: [[[loginTopology, devicesTopology], guardTopology], toModulesTopology(modules)],
88
+ };
89
+ }
90
+ }
91
+ function toModulesTopology(modules) {
92
+ // We always include a modules topology, even if there are no modules
93
+ // in that case we just add a signer with address 0 and no weight
94
+ if (modules.length === 0) {
95
+ return {
96
+ type: 'signer',
97
+ address: '0x0000000000000000000000000000000000000000',
98
+ weight: 0n,
99
+ };
100
+ }
101
+ return Config.flatLeavesToTopology(modules);
102
+ }
103
+ function fromModulesTopology(topology) {
104
+ let modules = [];
105
+ if (Config.isNode(topology)) {
106
+ modules = [...fromModulesTopology(topology[0]), ...fromModulesTopology(topology[1])];
107
+ }
108
+ else if (Config.isSapientSignerLeaf(topology)) {
109
+ modules.push(topology);
110
+ }
111
+ else if (Config.isSignerLeaf(topology)) {
112
+ // This signals that the wallet has no modules, so we just ignore it
113
+ if (topology.address !== '0x0000000000000000000000000000000000000000') {
114
+ throw new Error('signer-leaf-not-allowed-in-modules-topology');
115
+ }
116
+ }
117
+ else {
118
+ throw new Error('unknown-modules-topology-format');
119
+ }
120
+ return modules;
121
+ }
122
+ function fromConfig(config) {
123
+ if (config.threshold === 1n) {
124
+ if (Config.isNode(config.topology) && Config.isNode(config.topology[0])) {
125
+ return {
126
+ loginTopology: config.topology[0][0],
127
+ devicesTopology: config.topology[0][1],
128
+ modules: fromModulesTopology(config.topology[1]),
129
+ };
130
+ }
131
+ else {
132
+ throw new Error('unknown-config-format');
133
+ }
134
+ }
135
+ else if (config.threshold === 2n) {
136
+ if (Config.isNode(config.topology) && Config.isNode(config.topology[0]) && Config.isNode(config.topology[0][0])) {
137
+ return {
138
+ loginTopology: config.topology[0][0][0],
139
+ devicesTopology: config.topology[0][0][1],
140
+ guardTopology: config.topology[0][1],
141
+ modules: fromModulesTopology(config.topology[1]),
142
+ };
143
+ }
144
+ else {
145
+ throw new Error('unknown-config-format');
146
+ }
147
+ }
148
+ throw new Error('unknown-config-format');
149
+ }
150
+ export class Wallets {
151
+ shared;
152
+ walletSelectionUiHandler = null;
153
+ constructor(shared) {
154
+ this.shared = shared;
155
+ }
156
+ async exists(wallet) {
157
+ return this.shared.databases.manager.get(wallet).then((r) => r !== undefined);
158
+ }
159
+ async get(walletAddress) {
160
+ return await this.shared.databases.manager.get(walletAddress);
161
+ }
162
+ async list() {
163
+ return this.shared.databases.manager.list();
164
+ }
165
+ registerWalletSelector(handler) {
166
+ if (this.walletSelectionUiHandler) {
167
+ throw new Error('wallet-selector-already-registered');
168
+ }
169
+ this.walletSelectionUiHandler = handler;
170
+ return () => {
171
+ this.unregisterWalletSelector(handler);
172
+ };
173
+ }
174
+ unregisterWalletSelector(handler) {
175
+ if (handler && this.walletSelectionUiHandler !== handler) {
176
+ throw new Error('wallet-selector-not-registered');
177
+ }
178
+ this.walletSelectionUiHandler = null;
179
+ }
180
+ onWalletsUpdate(cb, trigger) {
181
+ const undo = this.shared.databases.manager.addListener(() => {
182
+ this.list().then((wallets) => {
183
+ cb(wallets);
184
+ });
185
+ });
186
+ if (trigger) {
187
+ this.list().then((wallets) => {
188
+ cb(wallets);
189
+ });
190
+ }
191
+ return undo;
192
+ }
193
+ async prepareSignUp(args) {
194
+ switch (args.kind) {
195
+ case 'passkey':
196
+ const passkeySigner = await Signers.Passkey.Passkey.create(this.shared.sequence.extensions, {
197
+ stateProvider: this.shared.sequence.stateProvider,
198
+ });
199
+ this.shared.modules.logger.log('Created new passkey signer:', passkeySigner.address);
200
+ return {
201
+ signer: passkeySigner,
202
+ extra: {
203
+ signerKind: Kinds.LoginPasskey,
204
+ },
205
+ };
206
+ case 'mnemonic':
207
+ const mnemonicSigner = MnemonicHandler.toSigner(args.mnemonic);
208
+ if (!mnemonicSigner) {
209
+ throw new Error('invalid-mnemonic');
210
+ }
211
+ this.shared.modules.logger.log('Created new mnemonic signer:', mnemonicSigner.address);
212
+ return {
213
+ signer: mnemonicSigner,
214
+ extra: {
215
+ signerKind: Kinds.LoginMnemonic,
216
+ },
217
+ };
218
+ case 'email-otp': {
219
+ const handler = this.shared.handlers.get(Kinds.LoginEmailOtp);
220
+ if (!handler) {
221
+ throw new Error('email-otp-handler-not-registered');
222
+ }
223
+ const signer = await handler.getSigner(args.email);
224
+ this.shared.modules.logger.log('Created new email otp signer:', signer.address);
225
+ return {
226
+ signer,
227
+ extra: {
228
+ signerKind: Kinds.LoginEmailOtp,
229
+ },
230
+ };
231
+ }
232
+ case 'google-pkce':
233
+ case 'apple': {
234
+ const handler = this.shared.handlers.get('login-' + args.kind);
235
+ if (!handler) {
236
+ throw new Error('handler-not-registered');
237
+ }
238
+ const [signer, metadata] = await handler.completeAuth(args.commitment, args.code);
239
+ this.shared.modules.logger.log('Created new auth code pkce signer:', signer.address);
240
+ return {
241
+ signer,
242
+ extra: {
243
+ signerKind: 'login-' + args.kind,
244
+ },
245
+ };
246
+ }
247
+ }
248
+ }
249
+ async startSignUpWithRedirect(args) {
250
+ const handler = this.shared.handlers.get('login-' + args.kind);
251
+ if (!handler) {
252
+ throw new Error('handler-not-registered');
253
+ }
254
+ return handler.commitAuth(args.target, true);
255
+ }
256
+ async completeRedirect(args) {
257
+ const commitment = await this.shared.databases.authCommitments.get(args.state);
258
+ if (!commitment) {
259
+ throw new Error('invalid-state');
260
+ }
261
+ if (commitment.isSignUp) {
262
+ await this.signUp({
263
+ kind: commitment.kind,
264
+ commitment,
265
+ code: args.code,
266
+ noGuard: args.noGuard,
267
+ target: commitment.target,
268
+ isRedirect: true,
269
+ });
270
+ }
271
+ else {
272
+ const handler = this.shared.handlers.get('login-' + commitment.kind);
273
+ if (!handler) {
274
+ throw new Error('handler-not-registered');
275
+ }
276
+ await handler.completeAuth(commitment, args.code);
277
+ }
278
+ return commitment.target;
279
+ }
280
+ async signUp(args) {
281
+ const loginSigner = await this.prepareSignUp(args);
282
+ // If there is an existing wallet callback, we check if any wallet already exist for this login signer
283
+ if (this.walletSelectionUiHandler) {
284
+ const existingWallets = await State.getWalletsFor(this.shared.sequence.stateProvider, loginSigner.signer);
285
+ if (existingWallets.length > 0) {
286
+ const result = await this.walletSelectionUiHandler({
287
+ existingWallets: existingWallets.map((w) => w.wallet),
288
+ signerAddress: await loginSigner.signer.address,
289
+ context: isAuthCodeArgs(args)
290
+ ? {
291
+ isRedirect: args.isRedirect,
292
+ target: args.target,
293
+ }
294
+ : {
295
+ isRedirect: false,
296
+ },
297
+ });
298
+ if (result) {
299
+ // A wallet was selected, we can exit early
300
+ return;
301
+ }
302
+ }
303
+ }
304
+ else {
305
+ console.warn('No wallet selector registered, creating a new wallet');
306
+ }
307
+ // Create the first session
308
+ const device = await this.shared.modules.devices.create();
309
+ if (!args.noGuard && !this.shared.sequence.defaultGuardTopology) {
310
+ throw new Error('guard is required for signup');
311
+ }
312
+ // Build the login tree
313
+ const loginSignerAddress = await loginSigner.signer.address;
314
+ const loginTopology = buildCappedTree([
315
+ {
316
+ address: loginSignerAddress,
317
+ imageHash: Signers.isSapientSigner(loginSigner.signer) ? await loginSigner.signer.imageHash : undefined,
318
+ },
319
+ ]);
320
+ const devicesTopology = buildCappedTree([{ address: device.address }]);
321
+ const guardTopology = args.noGuard
322
+ ? undefined
323
+ : buildCappedTreeFromTopology(1n, this.shared.sequence.defaultGuardTopology);
324
+ // TODO: Add recovery module
325
+ // TODO: Add smart sessions module
326
+ // Placeholder
327
+ let modules = [];
328
+ if (!args.noSessionManager) {
329
+ // Calculate image hash with the identity signer
330
+ const sessionsTopology = SessionConfig.emptySessionsTopology(loginSignerAddress);
331
+ // Store this tree in the state provider
332
+ const sessionsConfigTree = SessionConfig.sessionsTopologyToConfigurationTree(sessionsTopology);
333
+ this.shared.sequence.stateProvider.saveTree(sessionsConfigTree);
334
+ // Prepare the configuration leaf
335
+ const sessionsImageHash = GenericTree.hash(sessionsConfigTree);
336
+ modules.push({
337
+ ...this.shared.sequence.defaultSessionsTopology,
338
+ imageHash: sessionsImageHash,
339
+ });
340
+ }
341
+ if (!args.noRecovery) {
342
+ await this.shared.modules.recovery.initRecoveryModule(modules, device.address);
343
+ }
344
+ // Create initial configuration
345
+ const initialConfiguration = toConfig(0n, loginTopology, devicesTopology, modules, guardTopology);
346
+ console.log('initialConfiguration', initialConfiguration);
347
+ // Create wallet
348
+ const wallet = await CoreWallet.fromConfiguration(initialConfiguration, {
349
+ context: this.shared.sequence.context,
350
+ stateProvider: this.shared.sequence.stateProvider,
351
+ guest: this.shared.sequence.guest,
352
+ });
353
+ this.shared.modules.logger.log('Created new sequence wallet:', wallet.address);
354
+ // Sign witness using device signer
355
+ await this.shared.modules.devices.witness(device.address, wallet.address);
356
+ // Sign witness using the passkey signer
357
+ await loginSigner.signer.witness(this.shared.sequence.stateProvider, wallet.address, loginSigner.extra);
358
+ // Save entry in the manager db
359
+ await this.shared.databases.manager.set({
360
+ address: wallet.address,
361
+ status: 'ready',
362
+ loginDate: new Date().toISOString(),
363
+ device: device.address,
364
+ loginType: loginSigner.extra.signerKind,
365
+ useGuard: !args.noGuard,
366
+ });
367
+ return wallet.address;
368
+ }
369
+ async getConfigurationParts(address) {
370
+ const wallet = new CoreWallet(address, {
371
+ context: this.shared.sequence.context,
372
+ stateProvider: this.shared.sequence.stateProvider,
373
+ guest: this.shared.sequence.guest,
374
+ });
375
+ const status = await wallet.getStatus();
376
+ return fromConfig(status.configuration);
377
+ }
378
+ async requestConfigurationUpdate(address, changes, action, origin) {
379
+ const wallet = new CoreWallet(address, {
380
+ context: this.shared.sequence.context,
381
+ stateProvider: this.shared.sequence.stateProvider,
382
+ guest: this.shared.sequence.guest,
383
+ });
384
+ const status = await wallet.getStatus();
385
+ const { loginTopology, devicesTopology, modules, guardTopology } = fromConfig(status.configuration);
386
+ const nextLoginTopology = changes.loginTopology ?? loginTopology;
387
+ const nextDevicesTopology = changes.devicesTopology ?? devicesTopology;
388
+ const nextModules = changes.modules ?? modules;
389
+ const nextGuardTopology = changes.guardTopology ?? guardTopology;
390
+ const envelope = await wallet.prepareUpdate(toConfig(status.configuration.checkpoint + 1n, nextLoginTopology, nextDevicesTopology, nextModules, nextGuardTopology));
391
+ const requestId = await this.shared.modules.signatures.request(envelope, action, {
392
+ origin,
393
+ });
394
+ return requestId;
395
+ }
396
+ async completeConfigurationUpdate(requestId) {
397
+ const request = await this.shared.modules.signatures.get(requestId);
398
+ if (!Payload.isConfigUpdate(request.envelope.payload)) {
399
+ throw new Error('invalid-request-payload');
400
+ }
401
+ if (!Envelope.reachedThreshold(request.envelope)) {
402
+ throw new Error('insufficient-weight');
403
+ }
404
+ const wallet = new CoreWallet(request.wallet, {
405
+ context: this.shared.sequence.context,
406
+ stateProvider: this.shared.sequence.stateProvider,
407
+ guest: this.shared.sequence.guest,
408
+ });
409
+ await wallet.submitUpdate(request.envelope);
410
+ await this.shared.modules.signatures.complete(requestId);
411
+ }
412
+ async login(args) {
413
+ if (isLoginToWalletArgs(args)) {
414
+ const prevWallet = await this.exists(args.wallet);
415
+ if (prevWallet) {
416
+ throw new Error('wallet-already-logged-in');
417
+ }
418
+ const device = await this.shared.modules.devices.create();
419
+ const { devicesTopology, modules, guardTopology } = await this.getConfigurationParts(args.wallet);
420
+ // Witness the wallet
421
+ await this.shared.modules.devices.witness(device.address, args.wallet);
422
+ // Add device to devices topology
423
+ const prevDevices = Config.getSigners(devicesTopology);
424
+ if (prevDevices.sapientSigners.length > 0) {
425
+ throw new Error('found-sapient-signer-in-devices-topology');
426
+ }
427
+ if (!prevDevices.isComplete) {
428
+ throw new Error('devices-topology-incomplete');
429
+ }
430
+ const nextDevicesTopology = buildCappedTree([
431
+ ...prevDevices.signers
432
+ .filter((x) => x !== '0x0000000000000000000000000000000000000000')
433
+ .map((x) => ({ address: x })),
434
+ ...prevDevices.sapientSigners.map((x) => ({ address: x.address, imageHash: x.imageHash })),
435
+ { address: device.address },
436
+ ]);
437
+ if (this.shared.modules.recovery.hasRecoveryModule(modules)) {
438
+ await this.shared.modules.recovery.addRecoverySignerToModules(modules, device.address);
439
+ }
440
+ await this.shared.databases.manager.set({
441
+ address: args.wallet,
442
+ status: 'logging-in',
443
+ loginDate: new Date().toISOString(),
444
+ device: device.address,
445
+ loginType: 'wallet',
446
+ useGuard: guardTopology !== undefined,
447
+ });
448
+ return this.requestConfigurationUpdate(args.wallet, {
449
+ devicesTopology: nextDevicesTopology,
450
+ modules,
451
+ }, 'login', 'wallet-webapp');
452
+ }
453
+ if (isLoginToMnemonicArgs(args)) {
454
+ const mnemonicSigner = MnemonicHandler.toSigner(args.mnemonic);
455
+ if (!mnemonicSigner) {
456
+ throw new Error('invalid-mnemonic');
457
+ }
458
+ const wallets = await State.getWalletsFor(this.shared.sequence.stateProvider, mnemonicSigner);
459
+ if (wallets.length === 0) {
460
+ throw new Error('no-wallets-found');
461
+ }
462
+ const wallet = await args.selectWallet(wallets.map((w) => w.wallet));
463
+ if (!wallets.some((w) => Address.isEqual(w.wallet, wallet))) {
464
+ throw new Error('wallet-not-found');
465
+ }
466
+ return this.login({ wallet });
467
+ }
468
+ if (isLoginToPasskeyArgs(args)) {
469
+ const passkeySigner = await Signers.Passkey.Passkey.find(this.shared.sequence.stateProvider, this.shared.sequence.extensions);
470
+ if (!passkeySigner) {
471
+ throw new Error('no-passkey-found');
472
+ }
473
+ const wallets = await State.getWalletsFor(this.shared.sequence.stateProvider, passkeySigner);
474
+ if (wallets.length === 0) {
475
+ throw new Error('no-wallets-found');
476
+ }
477
+ const wallet = await args.selectWallet(wallets.map((w) => w.wallet));
478
+ if (!wallets.some((w) => Address.isEqual(w.wallet, wallet))) {
479
+ throw new Error('wallet-not-found');
480
+ }
481
+ return this.login({ wallet });
482
+ }
483
+ throw new Error('invalid-login-args');
484
+ }
485
+ async completeLogin(requestId) {
486
+ const request = await this.shared.modules.signatures.get(requestId);
487
+ const walletEntry = await this.shared.databases.manager.get(request.wallet);
488
+ if (!walletEntry) {
489
+ throw new Error('login-for-wallet-not-found');
490
+ }
491
+ await this.completeConfigurationUpdate(requestId);
492
+ // Save entry in the manager db
493
+ await this.shared.databases.manager.set({
494
+ ...walletEntry,
495
+ status: 'ready',
496
+ loginDate: new Date().toISOString(),
497
+ });
498
+ }
499
+ async logout(wallet, options) {
500
+ const walletEntry = await this.shared.databases.manager.get(wallet);
501
+ if (!walletEntry) {
502
+ throw new Error('wallet-not-found');
503
+ }
504
+ // Prevent starting logout if already logging out or not ready
505
+ if (walletEntry.status !== 'ready') {
506
+ console.warn(`Logout called on wallet ${wallet} with status ${walletEntry.status}. Aborting.`);
507
+ throw new Error(`Wallet is not in 'ready' state for logout (current: ${walletEntry.status})`);
508
+ }
509
+ if (options?.skipRemoveDevice) {
510
+ await Promise.all([
511
+ this.shared.databases.manager.del(wallet),
512
+ this.shared.modules.devices.remove(walletEntry.device),
513
+ ]);
514
+ return undefined;
515
+ }
516
+ const device = await this.shared.modules.devices.get(walletEntry.device);
517
+ if (!device) {
518
+ throw new Error('device-not-found');
519
+ }
520
+ const { devicesTopology, modules } = await this.getConfigurationParts(wallet);
521
+ const nextDevicesTopology = buildCappedTree([
522
+ ...Config.getSigners(devicesTopology)
523
+ .signers.filter((x) => x !== '0x0000000000000000000000000000000000000000' && x !== device.address)
524
+ .map((x) => ({ address: x })),
525
+ ...Config.getSigners(devicesTopology).sapientSigners,
526
+ ]);
527
+ // Remove device from the recovery topology, if it exists
528
+ if (this.shared.modules.recovery.hasRecoveryModule(modules)) {
529
+ await this.shared.modules.recovery.removeRecoverySignerFromModules(modules, device.address);
530
+ }
531
+ const requestId = await this.requestConfigurationUpdate(wallet, {
532
+ devicesTopology: nextDevicesTopology,
533
+ modules,
534
+ }, 'logout', 'wallet-webapp');
535
+ await this.shared.databases.manager.set({ ...walletEntry, status: 'logging-out' });
536
+ return requestId;
537
+ }
538
+ async completeLogout(requestId, options) {
539
+ const request = await this.shared.modules.signatures.get(requestId);
540
+ const walletEntry = await this.shared.databases.manager.get(request.wallet);
541
+ if (!walletEntry) {
542
+ throw new Error('wallet-not-found');
543
+ }
544
+ // Wallet entry should ideally be 'logging-out' here, but we proceed regardless
545
+ if (walletEntry.status !== 'logging-out') {
546
+ this.shared.modules.logger.log(`Warning: Wallet ${request.wallet} status was ${walletEntry.status} during completeLogout.`);
547
+ }
548
+ await this.completeConfigurationUpdate(requestId);
549
+ await this.shared.databases.manager.del(request.wallet);
550
+ await this.shared.modules.devices.remove(walletEntry.device);
551
+ }
552
+ async getConfiguration(wallet) {
553
+ const walletObject = new CoreWallet(wallet, {
554
+ context: this.shared.sequence.context,
555
+ stateProvider: this.shared.sequence.stateProvider,
556
+ guest: this.shared.sequence.guest,
557
+ });
558
+ const status = await walletObject.getStatus();
559
+ const raw = fromConfig(status.configuration);
560
+ const deviceSigners = Config.getSigners(raw.devicesTopology);
561
+ const loginSigners = Config.getSigners(raw.loginTopology);
562
+ return {
563
+ devices: await this.shared.modules.signers.resolveKinds(wallet, [
564
+ ...deviceSigners.signers,
565
+ ...deviceSigners.sapientSigners,
566
+ ]),
567
+ login: await this.shared.modules.signers.resolveKinds(wallet, [
568
+ ...loginSigners.signers,
569
+ ...loginSigners.sapientSigners,
570
+ ]),
571
+ raw,
572
+ };
573
+ }
574
+ async getNonce(chainId, address, space) {
575
+ const wallet = new CoreWallet(address, {
576
+ context: this.shared.sequence.context,
577
+ stateProvider: this.shared.sequence.stateProvider,
578
+ guest: this.shared.sequence.guest,
579
+ });
580
+ const network = this.shared.sequence.networks.find((n) => n.chainId === chainId);
581
+ if (!network) {
582
+ throw new Error('network-not-found');
583
+ }
584
+ const provider = Provider.from(RpcTransport.fromHttp(network.rpc));
585
+ return wallet.getNonce(provider, space);
586
+ }
587
+ async getOnchainConfiguration(wallet, chainId) {
588
+ const walletObject = new CoreWallet(wallet, {
589
+ context: this.shared.sequence.context,
590
+ stateProvider: this.shared.sequence.stateProvider,
591
+ guest: this.shared.sequence.guest,
592
+ });
593
+ const network = this.shared.sequence.networks.find((n) => n.chainId === chainId);
594
+ if (!network) {
595
+ throw new Error('network-not-found');
596
+ }
597
+ const provider = Provider.from(RpcTransport.fromHttp(network.rpc));
598
+ const status = await walletObject.getStatus(provider);
599
+ const onchainConfiguration = await this.shared.sequence.stateProvider.getConfiguration(status.onChainImageHash);
600
+ if (!onchainConfiguration) {
601
+ throw new Error('onchain-configuration-not-found');
602
+ }
603
+ const raw = fromConfig(status.configuration);
604
+ const deviceSigners = Config.getSigners(raw.devicesTopology);
605
+ const loginSigners = Config.getSigners(raw.loginTopology);
606
+ return {
607
+ devices: await this.shared.modules.signers.resolveKinds(wallet, [
608
+ ...deviceSigners.signers,
609
+ ...deviceSigners.sapientSigners,
610
+ ]),
611
+ login: await this.shared.modules.signers.resolveKinds(wallet, [
612
+ ...loginSigners.signers,
613
+ ...loginSigners.sapientSigners,
614
+ ]),
615
+ raw,
616
+ };
617
+ }
618
+ async isUpdatedOnchain(wallet, chainId) {
619
+ const walletObject = new CoreWallet(wallet, {
620
+ context: this.shared.sequence.context,
621
+ stateProvider: this.shared.sequence.stateProvider,
622
+ guest: this.shared.sequence.guest,
623
+ });
624
+ const network = this.shared.sequence.networks.find((n) => n.chainId === chainId);
625
+ if (!network) {
626
+ throw new Error('network-not-found');
627
+ }
628
+ const provider = Provider.from(RpcTransport.fromHttp(network.rpc));
629
+ const onchainStatus = await walletObject.getStatus(provider);
630
+ return onchainStatus.imageHash === onchainStatus.onChainImageHash;
631
+ }
632
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@0xsequence/wallet-wdk",
3
+ "version": "0.0.0-20250520201059",
4
+ "license": "Apache-2.0",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "private": false,
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.13.9",
18
+ "dotenv": "^16.4.7",
19
+ "fake-indexeddb": "^6.0.0",
20
+ "happy-dom": "^13.2.0",
21
+ "typescript": "^5.7.3",
22
+ "vitest": "^3.1.2",
23
+ "@repo/typescript-config": "^0.0.0-20250520201059"
24
+ },
25
+ "dependencies": {
26
+ "@0xsequence/tee-verifier": "^0.1.0",
27
+ "idb": "^7.1.1",
28
+ "jwt-decode": "^4.0.0",
29
+ "ox": "^0.7.0",
30
+ "uuid": "^11.1.0",
31
+ "@0xsequence/wallet-primitives": "^0.0.0-20250520201059",
32
+ "@0xsequence/wallet-core": "^0.0.0-20250520201059",
33
+ "@0xsequence/identity-instrument": "^0.0.0-20250520201059"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc",
37
+ "dev": "tsc --watch",
38
+ "test": "vitest run"
39
+ }
40
+ }
@@ -0,0 +1,26 @@
1
+ import { Generic } from './generic.js'
2
+
3
+ const TABLE_NAME = 'auth-commitments'
4
+
5
+ export type AuthCommitment = {
6
+ id: string
7
+ kind: 'google-pkce' | 'apple'
8
+ metadata: { [key: string]: string }
9
+ verifier?: string
10
+ challenge?: string
11
+ target: string
12
+ isSignUp: boolean
13
+ signer?: string
14
+ }
15
+
16
+ export class AuthCommitments extends Generic<AuthCommitment, 'id'> {
17
+ constructor(dbName: string = 'sequence-auth-commitments') {
18
+ super(dbName, TABLE_NAME, 'id', [
19
+ (db: IDBDatabase) => {
20
+ if (!db.objectStoreNames.contains(TABLE_NAME)) {
21
+ db.createObjectStore(TABLE_NAME)
22
+ }
23
+ },
24
+ ])
25
+ }
26
+ }