@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 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/sequence/messages.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAsC,MAAM,IAAI,CAAA;AAEhE,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAmC,MAAM,4BAA4B,CAAA;AAErG,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE9B,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAI1B,GAAG,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAIlD,yBAAyB;IASjC,OAAO,CACX,IAAI,EAAE,OAAO,CAAC,OAAO,EACrB,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QACR,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,GACA,OAAO,CAAC,MAAM,CAAC;IAyBZ,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqD7D,gBAAgB,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO;IAYrE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO;IAY9E,MAAM,CAAC,oBAAoB,EAAE,MAAM;CAS1C"}
@@ -0,0 +1,115 @@
1
+ import { Envelope, Wallet } from '@0xsequence/wallet-core';
2
+ import { Payload } from '@0xsequence/wallet-primitives';
3
+ import { Hex, Provider, RpcTransport } from 'ox';
4
+ import { v7 as uuidv7 } from 'uuid';
5
+ export class Messages {
6
+ shared;
7
+ constructor(shared) {
8
+ this.shared = shared;
9
+ }
10
+ async list() {
11
+ return this.shared.databases.messages.list();
12
+ }
13
+ async get(messageOrSignatureId) {
14
+ return this.getByMessageOrSignatureId(messageOrSignatureId);
15
+ }
16
+ async getByMessageOrSignatureId(messageOrSignatureId) {
17
+ const messages = await this.list();
18
+ const message = messages.find((m) => m.id === messageOrSignatureId || m.signatureId === messageOrSignatureId);
19
+ if (!message) {
20
+ throw new Error(`Message ${messageOrSignatureId} not found`);
21
+ }
22
+ return message;
23
+ }
24
+ async request(from, message, chainId, options) {
25
+ const wallet = new Wallet(from, { stateProvider: this.shared.sequence.stateProvider });
26
+ // Prepare message payload
27
+ const envelope = await wallet.prepareMessageSignature(message, chainId ?? 0n);
28
+ // Prepare signature request
29
+ const signatureRequest = await this.shared.modules.signatures.request(envelope, 'sign-message', {
30
+ origin: options?.source,
31
+ });
32
+ const id = uuidv7();
33
+ await this.shared.databases.messages.set({
34
+ id,
35
+ wallet: from,
36
+ message,
37
+ envelope,
38
+ source: options?.source ?? 'unknown',
39
+ status: 'requested',
40
+ signatureId: signatureRequest,
41
+ });
42
+ return signatureRequest;
43
+ }
44
+ async complete(messageOrSignatureId) {
45
+ const message = await this.getByMessageOrSignatureId(messageOrSignatureId);
46
+ if (message.status === 'signed') {
47
+ // Return the message signature
48
+ return message.messageSignature;
49
+ }
50
+ const messageId = message.id;
51
+ const signature = await this.shared.modules.signatures.get(message.signatureId);
52
+ if (!signature) {
53
+ throw new Error(`Signature ${message.signatureId} not found for message ${messageId}`);
54
+ }
55
+ if (!Payload.isMessage(message.envelope.payload) || !Payload.isMessage(signature.envelope.payload)) {
56
+ throw new Error(`Message ${messageId} is not a message payload`);
57
+ }
58
+ if (!Envelope.isSigned(signature.envelope)) {
59
+ throw new Error(`Message ${messageId} is not signed`);
60
+ }
61
+ const signatureEnvelope = signature.envelope;
62
+ const { weight, threshold } = Envelope.weightOf(signatureEnvelope);
63
+ if (weight < threshold) {
64
+ throw new Error(`Message ${messageId} has insufficient weight`);
65
+ }
66
+ // Get the provider for the message chain
67
+ let provider;
68
+ if (message.envelope.chainId !== 0n) {
69
+ const network = this.shared.sequence.networks.find((network) => network.chainId === message.envelope.chainId);
70
+ if (!network) {
71
+ throw new Error(`Network not found for ${message.envelope.chainId}`);
72
+ }
73
+ const transport = RpcTransport.fromHttp(network.rpc);
74
+ provider = Provider.from(transport);
75
+ }
76
+ const wallet = new Wallet(message.wallet, { stateProvider: this.shared.sequence.stateProvider });
77
+ const messageSignature = Hex.from(await wallet.buildMessageSignature(signatureEnvelope, provider));
78
+ await this.shared.databases.messages.set({
79
+ ...message,
80
+ envelope: signature.envelope,
81
+ status: 'signed',
82
+ messageSignature,
83
+ });
84
+ await this.shared.modules.signatures.complete(signature.id);
85
+ return messageSignature;
86
+ }
87
+ onMessagesUpdate(cb, trigger) {
88
+ const undo = this.shared.databases.messages.addListener(() => {
89
+ this.list().then((l) => cb(l));
90
+ });
91
+ if (trigger) {
92
+ this.list().then((l) => cb(l));
93
+ }
94
+ return undo;
95
+ }
96
+ onMessageUpdate(messageId, cb, trigger) {
97
+ const undo = this.shared.databases.messages.addListener(() => {
98
+ this.get(messageId).then((t) => cb(t));
99
+ });
100
+ if (trigger) {
101
+ this.get(messageId).then((t) => cb(t));
102
+ }
103
+ return undo;
104
+ }
105
+ async delete(messageOrSignatureId) {
106
+ try {
107
+ const message = await this.getByMessageOrSignatureId(messageOrSignatureId);
108
+ await this.shared.databases.signatures.del(message.signatureId);
109
+ await this.shared.databases.messages.del(message.id);
110
+ }
111
+ catch (error) {
112
+ // Ignore
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,30 @@
1
+ import { Config, Payload } from '@0xsequence/wallet-primitives';
2
+ import { Shared } from './manager.js';
3
+ import { Address, Hex } from 'ox';
4
+ import { RecoverySigner } from './types/signer.js';
5
+ import { QueuedRecoveryPayload } from './types/recovery.js';
6
+ export declare class Recovery {
7
+ private readonly shared;
8
+ constructor(shared: Shared);
9
+ initialize(): void;
10
+ private updateRecoveryModule;
11
+ initRecoveryModule(modules: Config.SapientSignerLeaf[], address: Address.Address): Promise<void>;
12
+ hasRecoveryModule(modules: Config.SapientSignerLeaf[]): boolean;
13
+ addRecoverySignerToModules(modules: Config.SapientSignerLeaf[], address: Address.Address): Promise<void>;
14
+ removeRecoverySignerFromModules(modules: Config.SapientSignerLeaf[], address: Address.Address): Promise<void>;
15
+ addRecoveryMnemonic(wallet: Address.Address, mnemonic: string): Promise<string>;
16
+ addRecoverySigner(wallet: Address.Address, address: Address.Address): Promise<string>;
17
+ removeRecoverySigner(wallet: Address.Address, address: Address.Address): Promise<string>;
18
+ completeRecoveryUpdate(requestId: string): Promise<void>;
19
+ getRecoverySigners(address: Address.Address): Promise<RecoverySigner[] | undefined>;
20
+ queueRecoveryPayload(wallet: Address.Address, chainId: bigint, payload: Payload.Calls): Promise<string>;
21
+ completeRecoveryPayload(requestId: string): Promise<{
22
+ to: Address.Address;
23
+ data: Hex.Hex;
24
+ }>;
25
+ getQueuedRecoveryPayloads(wallet?: Address.Address): Promise<QueuedRecoveryPayload[]>;
26
+ onQueuedRecoveryPayloadsUpdate(wallet: Address.Address | undefined, cb: (payloads: QueuedRecoveryPayload[]) => void, trigger?: boolean): () => void;
27
+ updateQueuedRecoveryPayloads(): Promise<void>;
28
+ encodeRecoverySignature(imageHash: Hex.Hex, signer: Address.Address): Promise<import("ox/Bytes").Bytes>;
29
+ }
30
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../../src/sequence/recovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA2B,OAAO,EAAE,MAAM,+BAA+B,CAAA;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,GAAG,EAA0B,MAAM,IAAI,CAAA;AACzD,OAAO,EAAS,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAI3D,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C,UAAU;YAYI,oBAAoB;IAoCrB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;IA2B7F,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,iBAAiB,EAAE,GAAG,OAAO;IAIzD,0BAA0B,CAAC,OAAO,EAAE,MAAM,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;IAwBxF,+BAA+B,CAAC,OAAO,EAAE,MAAM,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;IAsB7F,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM;IAc7D,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;IAanE,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO;IAWtE,sBAAsB,CAAC,SAAS,EAAE,MAAM;IASxC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,GAAG,SAAS,CAAC;IAkCnF,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK;IAuCrF,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC;QAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAA;KAAE,CAAC;IAsC3F,yBAAyB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAS3F,8BAA8B,CAC5B,MAAM,EAAE,OAAO,CAAC,OAAO,GAAG,SAAS,EACnC,EAAE,EAAE,CAAC,QAAQ,EAAE,qBAAqB,EAAE,KAAK,IAAI,EAC/C,OAAO,CAAC,EAAE,OAAO;IAWb,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC;IA8F7C,uBAAuB,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO;CAgB1E"}
@@ -0,0 +1,314 @@
1
+ import { Config, Extensions, GenericTree, Payload } from '@0xsequence/wallet-primitives';
2
+ import { Address, Provider, RpcTransport } from 'ox';
3
+ import { Kinds } from './types/signer.js';
4
+ import { Envelope } from '@0xsequence/wallet-core';
5
+ import { Actions } from './types/index.js';
6
+ import { MnemonicHandler } from './handlers/mnemonic.js';
7
+ export class Recovery {
8
+ shared;
9
+ constructor(shared) {
10
+ this.shared = shared;
11
+ }
12
+ initialize() {
13
+ this.shared.modules.cron.registerJob('update-queued-recovery-payloads', 5 * 60 * 1000, // 5 minutes
14
+ async () => {
15
+ this.shared.modules.logger.log('Running job: update-queued-recovery-payloads');
16
+ await this.updateQueuedRecoveryPayloads();
17
+ });
18
+ this.shared.modules.logger.log('Recovery module initialized and job registered.');
19
+ }
20
+ async updateRecoveryModule(modules, transformer) {
21
+ const ext = this.shared.sequence.extensions.recovery;
22
+ const idx = modules.findIndex((m) => m.address === ext);
23
+ if (idx === -1) {
24
+ return;
25
+ }
26
+ const sapientSigner = modules[idx];
27
+ if (!sapientSigner) {
28
+ throw new Error('recovery-module-not-found');
29
+ }
30
+ const genericTree = await this.shared.sequence.stateProvider.getTree(sapientSigner.imageHash);
31
+ if (!genericTree) {
32
+ throw new Error('recovery-module-tree-not-found');
33
+ }
34
+ const tree = Extensions.Recovery.fromGenericTree(genericTree);
35
+ const { leaves, isComplete } = Extensions.Recovery.getRecoveryLeaves(tree);
36
+ if (!isComplete) {
37
+ throw new Error('recovery-module-tree-incomplete');
38
+ }
39
+ const nextTree = Extensions.Recovery.fromRecoveryLeaves(transformer(leaves));
40
+ const nextGeneric = Extensions.Recovery.toGenericTree(nextTree);
41
+ await this.shared.sequence.stateProvider.saveTree(nextGeneric);
42
+ if (!modules[idx]) {
43
+ throw new Error('recovery-module-not-found-(unreachable)');
44
+ }
45
+ modules[idx].imageHash = GenericTree.hash(nextGeneric);
46
+ }
47
+ async initRecoveryModule(modules, address) {
48
+ if (this.hasRecoveryModule(modules)) {
49
+ throw new Error('recovery-module-already-initialized');
50
+ }
51
+ const recoveryTree = Extensions.Recovery.fromRecoveryLeaves([
52
+ {
53
+ type: 'leaf',
54
+ signer: address,
55
+ requiredDeltaTime: this.shared.sequence.defaultRecoverySettings.requiredDeltaTime,
56
+ minTimestamp: this.shared.sequence.defaultRecoverySettings.minTimestamp,
57
+ },
58
+ ]);
59
+ const recoveryGenericTree = Extensions.Recovery.toGenericTree(recoveryTree);
60
+ await this.shared.sequence.stateProvider.saveTree(recoveryGenericTree);
61
+ const recoveryImageHash = GenericTree.hash(recoveryGenericTree);
62
+ modules.push({
63
+ type: 'sapient-signer',
64
+ address: this.shared.sequence.extensions.recovery,
65
+ weight: 255n,
66
+ imageHash: recoveryImageHash,
67
+ });
68
+ }
69
+ hasRecoveryModule(modules) {
70
+ return modules.some((m) => Address.isEqual(m.address, this.shared.sequence.extensions.recovery));
71
+ }
72
+ async addRecoverySignerToModules(modules, address) {
73
+ if (!this.hasRecoveryModule(modules)) {
74
+ throw new Error('recovery-module-not-enabled');
75
+ }
76
+ await this.updateRecoveryModule(modules, (leaves) => {
77
+ if (leaves.some((l) => Address.isEqual(l.signer, address))) {
78
+ return leaves;
79
+ }
80
+ const filtered = leaves.filter((l) => !Address.isEqual(l.signer, '0x0000000000000000000000000000000000000000'));
81
+ return [
82
+ ...filtered,
83
+ {
84
+ type: 'leaf',
85
+ signer: address,
86
+ requiredDeltaTime: this.shared.sequence.defaultRecoverySettings.requiredDeltaTime,
87
+ minTimestamp: this.shared.sequence.defaultRecoverySettings.minTimestamp,
88
+ },
89
+ ];
90
+ });
91
+ }
92
+ async removeRecoverySignerFromModules(modules, address) {
93
+ if (!this.hasRecoveryModule(modules)) {
94
+ throw new Error('recovery-module-not-enabled');
95
+ }
96
+ await this.updateRecoveryModule(modules, (leaves) => {
97
+ const next = leaves.filter((l) => l.signer !== address);
98
+ if (next.length === 0) {
99
+ return [
100
+ {
101
+ type: 'leaf',
102
+ signer: '0x0000000000000000000000000000000000000000',
103
+ requiredDeltaTime: 0n,
104
+ minTimestamp: 0n,
105
+ },
106
+ ];
107
+ }
108
+ return next;
109
+ });
110
+ }
111
+ async addRecoveryMnemonic(wallet, mnemonic) {
112
+ const signer = MnemonicHandler.toSigner(mnemonic);
113
+ if (!signer) {
114
+ throw new Error('invalid-mnemonic');
115
+ }
116
+ await signer.witness(this.shared.sequence.stateProvider, wallet, {
117
+ isForRecovery: true,
118
+ signerKind: Kinds.LoginMnemonic,
119
+ });
120
+ return this.addRecoverySigner(wallet, signer.address);
121
+ }
122
+ async addRecoverySigner(wallet, address) {
123
+ const { modules } = await this.shared.modules.wallets.getConfigurationParts(wallet);
124
+ await this.addRecoverySignerToModules(modules, address);
125
+ return this.shared.modules.wallets.requestConfigurationUpdate(wallet, {
126
+ modules,
127
+ }, Actions.AddRecoverySigner, 'wallet-webapp');
128
+ }
129
+ async removeRecoverySigner(wallet, address) {
130
+ const { modules } = await this.shared.modules.wallets.getConfigurationParts(wallet);
131
+ await this.removeRecoverySignerFromModules(modules, address);
132
+ return this.shared.modules.wallets.requestConfigurationUpdate(wallet, { modules }, Actions.RemoveRecoverySigner, 'wallet-webapp');
133
+ }
134
+ async completeRecoveryUpdate(requestId) {
135
+ const request = await this.shared.modules.signatures.get(requestId);
136
+ if (request.action !== 'add-recovery-signer' && request.action !== 'remove-recovery-signer') {
137
+ throw new Error('invalid-recovery-update-action');
138
+ }
139
+ return this.shared.modules.wallets.completeConfigurationUpdate(requestId);
140
+ }
141
+ async getRecoverySigners(address) {
142
+ const { raw } = await this.shared.modules.wallets.getConfiguration(address);
143
+ const recoveryLeaf = raw.modules.find((m) => Address.isEqual(m.address, this.shared.sequence.extensions.recovery));
144
+ if (!recoveryLeaf) {
145
+ return undefined;
146
+ }
147
+ const recoveryGenericTree = await this.shared.sequence.stateProvider.getTree(recoveryLeaf.imageHash);
148
+ if (!recoveryGenericTree) {
149
+ throw new Error('recovery-module-tree-not-found');
150
+ }
151
+ const recoveryTree = Extensions.Recovery.fromGenericTree(recoveryGenericTree);
152
+ const { leaves, isComplete } = Extensions.Recovery.getRecoveryLeaves(recoveryTree);
153
+ if (!isComplete) {
154
+ throw new Error('recovery-module-tree-incomplete');
155
+ }
156
+ const kos = await this.shared.modules.signers.resolveKinds(address, leaves.map((l) => l.signer));
157
+ return leaves
158
+ .filter((l) => !Address.isEqual(l.signer, '0x0000000000000000000000000000000000000000'))
159
+ .map((l) => ({
160
+ address: l.signer,
161
+ kind: kos.find((s) => Address.isEqual(s.address, l.signer))?.kind || 'unknown',
162
+ isRecovery: true,
163
+ minTimestamp: l.minTimestamp,
164
+ requiredDeltaTime: l.requiredDeltaTime,
165
+ }));
166
+ }
167
+ async queueRecoveryPayload(wallet, chainId, payload) {
168
+ const signers = await this.getRecoverySigners(wallet);
169
+ if (!signers) {
170
+ throw new Error('recovery-signers-not-found');
171
+ }
172
+ const recoveryPayload = Payload.toRecovery(payload);
173
+ const simulatedTopology = Config.flatLeavesToTopology(signers.map((s) => ({
174
+ type: 'signer',
175
+ address: s.address,
176
+ weight: 1n,
177
+ })));
178
+ // Save both versions of the payload in parallel
179
+ await Promise.all([
180
+ this.shared.sequence.stateProvider.savePayload(wallet, payload, chainId),
181
+ this.shared.sequence.stateProvider.savePayload(wallet, recoveryPayload, chainId),
182
+ ]);
183
+ const requestId = await this.shared.modules.signatures.request({
184
+ wallet,
185
+ chainId,
186
+ configuration: {
187
+ threshold: 1n,
188
+ checkpoint: 0n,
189
+ topology: simulatedTopology,
190
+ },
191
+ payload: recoveryPayload,
192
+ }, 'recovery');
193
+ return requestId;
194
+ }
195
+ // TODO: Handle this transaction instead of just returning the to and data
196
+ async completeRecoveryPayload(requestId) {
197
+ const signature = await this.shared.modules.signatures.get(requestId);
198
+ if (signature.action !== 'recovery' || !Payload.isRecovery(signature.envelope.payload)) {
199
+ throw new Error('invalid-recovery-payload');
200
+ }
201
+ if (!Envelope.isSigned(signature.envelope)) {
202
+ throw new Error('recovery-payload-not-signed');
203
+ }
204
+ const { weight, threshold } = Envelope.weightOf(signature.envelope);
205
+ if (weight < threshold) {
206
+ throw new Error('recovery-payload-insufficient-weight');
207
+ }
208
+ // Find any valid signature
209
+ const validSignature = signature.envelope.signatures[0];
210
+ if (Envelope.isSapientSignature(validSignature)) {
211
+ throw new Error('recovery-payload-sapient-signatures-not-supported');
212
+ }
213
+ if (!validSignature) {
214
+ throw new Error('recovery-payload-no-valid-signature');
215
+ }
216
+ const calldata = Extensions.Recovery.encodeCalldata(signature.wallet, signature.envelope.payload, validSignature.address, validSignature.signature);
217
+ return {
218
+ to: this.shared.sequence.extensions.recovery,
219
+ data: calldata,
220
+ };
221
+ }
222
+ async getQueuedRecoveryPayloads(wallet) {
223
+ const all = await this.shared.databases.recovery.list();
224
+ if (wallet) {
225
+ return all.filter((p) => Address.isEqual(p.wallet, wallet));
226
+ }
227
+ return all;
228
+ }
229
+ onQueuedRecoveryPayloadsUpdate(wallet, cb, trigger) {
230
+ if (trigger) {
231
+ this.getQueuedRecoveryPayloads(wallet).then(cb);
232
+ }
233
+ return this.shared.databases.recovery.addListener(() => {
234
+ this.getQueuedRecoveryPayloads(wallet).then(cb);
235
+ });
236
+ }
237
+ async updateQueuedRecoveryPayloads() {
238
+ const wallets = await this.shared.modules.wallets.list();
239
+ if (wallets.length === 0) {
240
+ return;
241
+ }
242
+ // Create providers for each network
243
+ const providers = this.shared.sequence.networks.map((network) => ({
244
+ chainId: network.chainId,
245
+ provider: Provider.from(RpcTransport.fromHttp(network.rpc)),
246
+ }));
247
+ const seenInThisRun = new Set();
248
+ for (const wallet of wallets) {
249
+ // See if they have any recover signers
250
+ const signers = await this.getRecoverySigners(wallet.address);
251
+ if (!signers || signers.length === 0) {
252
+ continue;
253
+ }
254
+ // Now we need to fetch, for each signer and network, any queued recovery payloads
255
+ // TODO: This may benefit from multicall, but it is not urgent, as this happens in the background
256
+ for (const signer of signers) {
257
+ for (const { chainId, provider } of providers) {
258
+ const totalPayloads = await Extensions.Recovery.totalQueuedPayloads(provider, this.shared.sequence.extensions.recovery, wallet.address, signer.address);
259
+ for (let i = 0n; i < totalPayloads; i++) {
260
+ const payloadHash = await Extensions.Recovery.queuedPayloadHashOf(provider, this.shared.sequence.extensions.recovery, wallet.address, signer.address, i);
261
+ const timestamp = await Extensions.Recovery.timestampForQueuedPayload(provider, this.shared.sequence.extensions.recovery, wallet.address, signer.address, payloadHash);
262
+ const payload = await this.shared.sequence.stateProvider.getPayload(payloadHash);
263
+ // If ready, we need to check if it was executed already
264
+ // for this, we check if the wallet 77nonce for the given space
265
+ // is greater than the nonce in the payload
266
+ if (timestamp < Date.now() / 1000 && payload && Payload.isCalls(payload.payload)) {
267
+ const nonce = await this.shared.modules.wallets.getNonce(chainId, wallet.address, payload.payload.space);
268
+ if (nonce > i) {
269
+ continue;
270
+ }
271
+ }
272
+ // The id is the index + signer address + chainId + wallet address
273
+ const id = `${i}-${signer.address}-${chainId}-${wallet.address}`;
274
+ // Create a new payload
275
+ const payloadEntry = {
276
+ id,
277
+ index: i,
278
+ recoveryModule: this.shared.sequence.extensions.recovery,
279
+ wallet: wallet.address,
280
+ signer: signer.address,
281
+ chainId,
282
+ startTimestamp: timestamp,
283
+ endTimestamp: timestamp + signer.requiredDeltaTime,
284
+ payloadHash,
285
+ payload: payload?.payload,
286
+ };
287
+ await this.shared.databases.recovery.set(payloadEntry);
288
+ seenInThisRun.add(payloadEntry.id);
289
+ }
290
+ }
291
+ }
292
+ // Delete any unseen queued payloads as they are no longer relevant
293
+ const allQueuedPayloads = await this.shared.databases.recovery.list();
294
+ for (const payload of allQueuedPayloads) {
295
+ if (!seenInThisRun.has(payload.id)) {
296
+ await this.shared.databases.recovery.del(payload.id);
297
+ }
298
+ }
299
+ }
300
+ }
301
+ async encodeRecoverySignature(imageHash, signer) {
302
+ const genericTree = await this.shared.sequence.stateProvider.getTree(imageHash);
303
+ if (!genericTree) {
304
+ throw new Error('recovery-module-tree-not-found');
305
+ }
306
+ const tree = Extensions.Recovery.fromGenericTree(genericTree);
307
+ const allSigners = Extensions.Recovery.getRecoveryLeaves(tree).leaves.map((l) => l.signer);
308
+ if (!allSigners.includes(signer)) {
309
+ throw new Error('signer-not-found-in-recovery-module');
310
+ }
311
+ const trimmed = Extensions.Recovery.trimTopology(tree, signer);
312
+ return Extensions.Recovery.encodeTopology(trimmed);
313
+ }
314
+ }
@@ -0,0 +1,26 @@
1
+ import { Signers as CoreSigners } from '@0xsequence/wallet-core';
2
+ import { Attestation, Signature as SequenceSignature, SessionConfig } from '@0xsequence/wallet-primitives';
3
+ import { Address, Hex } from 'ox';
4
+ import { Shared } from './manager.js';
5
+ export type AuthorizeImplicitSessionArgs = {
6
+ target: string;
7
+ applicationData?: Hex.Hex;
8
+ };
9
+ export declare class Sessions {
10
+ private readonly shared;
11
+ private readonly sessionManagerAddresses;
12
+ constructor(shared: Shared, sessionManagerAddresses?: Address.Address[]);
13
+ getSessionTopology(walletAddress: Address.Address): Promise<SessionConfig.SessionsTopology>;
14
+ prepareAuthorizeImplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address, args: AuthorizeImplicitSessionArgs): Promise<string>;
15
+ completeAuthorizeImplicitSession(requestId: string): Promise<{
16
+ attestation: Attestation.Attestation;
17
+ signature: SequenceSignature.RSY;
18
+ }>;
19
+ addExplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address, permissions: CoreSigners.Session.ExplicitParams, origin?: string): Promise<string>;
20
+ removeExplicitSession(walletAddress: Address.Address, sessionAddress: Address.Address, origin?: string): Promise<string>;
21
+ addBlacklistAddress(walletAddress: Address.Address, address: Address.Address, origin?: string): Promise<string>;
22
+ removeBlacklistAddress(walletAddress: Address.Address, address: Address.Address, origin?: string): Promise<string>;
23
+ private prepareSessionUpdate;
24
+ completeSessionUpdate(requestId: string): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/sequence/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAY,MAAM,yBAAyB,CAAA;AAC1E,OAAO,EACL,WAAW,EAKX,SAAS,IAAI,iBAAiB,EAC9B,aAAa,EACd,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,OAAO,EAAe,GAAG,EAAE,MAAM,IAAI,CAAA;AAI9C,OAAO,EAA0B,MAAM,EAAE,MAAM,cAAc,CAAA;AAG7D,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,eAAe,CAAC,EAAE,GAAG,CAAC,GAAG,CAAA;CAC1B,CAAA;AAID,qBAAa,QAAQ;IAEjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,uBAAuB;gBADvB,MAAM,EAAE,MAAM,EACd,uBAAuB,GAAE,OAAO,CAAC,OAAO,EAAmC;IAGxF,kBAAkB,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,gBAAgB,CAAC;IAgB3F,+BAA+B,CACnC,aAAa,EAAE,OAAO,CAAC,OAAO,EAC9B,cAAc,EAAE,OAAO,CAAC,OAAO,EAC/B,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,MAAM,CAAC;IA+DZ,gCAAgC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QACjE,WAAW,EAAE,WAAW,CAAC,WAAW,CAAA;QACpC,SAAS,EAAE,iBAAiB,CAAC,GAAG,CAAA;KACjC,CAAC;IA8BI,kBAAkB,CACtB,aAAa,EAAE,OAAO,CAAC,OAAO,EAC9B,cAAc,EAAE,OAAO,CAAC,OAAO,EAC/B,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,cAAc,EAC/C,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC;IASZ,qBAAqB,CACzB,aAAa,EAAE,OAAO,CAAC,OAAO,EAC9B,cAAc,EAAE,OAAO,CAAC,OAAO,EAC/B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC;IASZ,mBAAmB,CACvB,aAAa,EAAE,OAAO,CAAC,OAAO,EAC9B,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC;IAMZ,sBAAsB,CAC1B,aAAa,EAAE,OAAO,CAAC,OAAO,EAC9B,OAAO,EAAE,OAAO,CAAC,OAAO,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC;YAMJ,oBAAoB;IAoC5B,qBAAqB,CAAC,SAAS,EAAE,MAAM;CAQ9C"}
@@ -0,0 +1,169 @@
1
+ import { Envelope } from '@0xsequence/wallet-core';
2
+ import { Constants, GenericTree, Payload, SessionConfig, } from '@0xsequence/wallet-primitives';
3
+ import { Address, Bytes, Hash, Hex } from 'ox';
4
+ import { AuthCodePkceHandler } from './handlers/authcode-pkce.js';
5
+ import { IdentityHandler, identityTypeToHex } from './handlers/identity.js';
6
+ import { ManagerOptionsDefaults } from './manager.js';
7
+ import { Actions } from './types/signature-request.js';
8
+ const DefaultSessionManagerAddresses = [Constants.DefaultSessionManager];
9
+ export class Sessions {
10
+ shared;
11
+ sessionManagerAddresses;
12
+ constructor(shared, sessionManagerAddresses = DefaultSessionManagerAddresses) {
13
+ this.shared = shared;
14
+ this.sessionManagerAddresses = sessionManagerAddresses;
15
+ }
16
+ async getSessionTopology(walletAddress) {
17
+ const { modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress);
18
+ const managerLeaf = modules.find((leaf) => this.sessionManagerAddresses.some((addr) => Address.isEqual(addr, leaf.address)));
19
+ if (!managerLeaf) {
20
+ throw new Error('Session manager not found');
21
+ }
22
+ const imageHash = managerLeaf.imageHash;
23
+ const tree = await this.shared.sequence.stateProvider.getTree(imageHash);
24
+ if (!tree) {
25
+ throw new Error('Session topology not found');
26
+ }
27
+ return SessionConfig.configurationTreeToSessionsTopology(tree);
28
+ }
29
+ async prepareAuthorizeImplicitSession(walletAddress, sessionAddress, args) {
30
+ const topology = await this.getSessionTopology(walletAddress);
31
+ const identitySignerAddress = SessionConfig.getIdentitySigner(topology);
32
+ if (!identitySignerAddress) {
33
+ throw new Error('No identity signer address found');
34
+ }
35
+ const identityKind = await this.shared.modules.signers.kindOf(walletAddress, identitySignerAddress);
36
+ if (!identityKind) {
37
+ throw new Error('No identity handler kind found');
38
+ }
39
+ const handler = this.shared.handlers.get(identityKind);
40
+ if (!handler) {
41
+ throw new Error('No identity handler found');
42
+ }
43
+ // Create the attestation to sign
44
+ let identityType;
45
+ let issuerHash = '0x';
46
+ let audienceHash = '0x';
47
+ if (handler instanceof IdentityHandler) {
48
+ identityType = handler.identityType;
49
+ if (handler instanceof AuthCodePkceHandler) {
50
+ issuerHash = Hash.keccak256(Hex.fromString(handler.issuer));
51
+ audienceHash = Hash.keccak256(Hex.fromString(handler.audience));
52
+ }
53
+ }
54
+ const attestation = {
55
+ approvedSigner: sessionAddress,
56
+ identityType: Bytes.fromHex(identityTypeToHex(identityType), { size: 4 }),
57
+ issuerHash: Bytes.fromHex(issuerHash, { size: 32 }),
58
+ audienceHash: Bytes.fromHex(audienceHash, { size: 32 }),
59
+ applicationData: Bytes.fromHex(args.applicationData ?? '0x'),
60
+ authData: {
61
+ redirectUrl: args.target,
62
+ },
63
+ };
64
+ // Fake the configuration with the single required signer
65
+ const configuration = {
66
+ threshold: 1n,
67
+ checkpoint: 0n,
68
+ topology: {
69
+ type: 'signer',
70
+ address: identitySignerAddress,
71
+ weight: 1n,
72
+ },
73
+ };
74
+ const envelope = {
75
+ payload: {
76
+ type: 'session-implicit-authorize',
77
+ sessionAddress,
78
+ attestation,
79
+ },
80
+ wallet: walletAddress,
81
+ chainId: 0n,
82
+ configuration,
83
+ };
84
+ // Request the signature from the identity handler
85
+ return this.shared.modules.signatures.request(envelope, 'session-implicit-authorize', {
86
+ origin: args.target,
87
+ });
88
+ }
89
+ async completeAuthorizeImplicitSession(requestId) {
90
+ // Get the updated signature request
91
+ const signatureRequest = await this.shared.modules.signatures.get(requestId);
92
+ if (signatureRequest.action !== 'session-implicit-authorize' ||
93
+ !Payload.isSessionImplicitAuthorize(signatureRequest.envelope.payload)) {
94
+ throw new Error('Invalid action');
95
+ }
96
+ if (!Envelope.isSigned(signatureRequest.envelope) || !Envelope.reachedThreshold(signatureRequest.envelope)) {
97
+ throw new Error('Envelope not signed or threshold not reached');
98
+ }
99
+ // Find any valid signature
100
+ const signature = signatureRequest.envelope.signatures[0];
101
+ if (!signature || !Envelope.isSignature(signature)) {
102
+ throw new Error('No valid signature found');
103
+ }
104
+ if (signature.signature.type !== 'hash') {
105
+ // Should never happen
106
+ throw new Error('Unsupported signature type');
107
+ }
108
+ return {
109
+ attestation: signatureRequest.envelope.payload.attestation,
110
+ signature: signature.signature,
111
+ };
112
+ }
113
+ async addExplicitSession(walletAddress, sessionAddress, permissions, origin) {
114
+ const topology = await this.getSessionTopology(walletAddress);
115
+ const newTopology = SessionConfig.addExplicitSession(topology, {
116
+ ...permissions,
117
+ signer: sessionAddress,
118
+ });
119
+ return this.prepareSessionUpdate(walletAddress, newTopology, origin);
120
+ }
121
+ async removeExplicitSession(walletAddress, sessionAddress, origin) {
122
+ const topology = await this.getSessionTopology(walletAddress);
123
+ const newTopology = SessionConfig.removeExplicitSession(topology, sessionAddress);
124
+ if (!newTopology) {
125
+ throw new Error('Session not found');
126
+ }
127
+ return this.prepareSessionUpdate(walletAddress, newTopology, origin);
128
+ }
129
+ async addBlacklistAddress(walletAddress, address, origin) {
130
+ const topology = await this.getSessionTopology(walletAddress);
131
+ const newTopology = SessionConfig.addToImplicitBlacklist(topology, address);
132
+ return this.prepareSessionUpdate(walletAddress, newTopology, origin);
133
+ }
134
+ async removeBlacklistAddress(walletAddress, address, origin) {
135
+ const topology = await this.getSessionTopology(walletAddress);
136
+ const newTopology = SessionConfig.removeFromImplicitBlacklist(topology, address);
137
+ return this.prepareSessionUpdate(walletAddress, newTopology, origin);
138
+ }
139
+ async prepareSessionUpdate(walletAddress, topology, origin = 'wallet-webapp') {
140
+ // Store the new configuration
141
+ const tree = SessionConfig.sessionsTopologyToConfigurationTree(topology);
142
+ await this.shared.sequence.stateProvider.saveTree(tree);
143
+ const newImageHash = GenericTree.hash(tree);
144
+ // Find the session manager in the old configuration
145
+ const { modules } = await this.shared.modules.wallets.getConfigurationParts(walletAddress);
146
+ const managerLeaf = modules.find((leaf) => this.sessionManagerAddresses.some((addr) => Address.isEqual(addr, leaf.address)));
147
+ if (!managerLeaf) {
148
+ // Missing. Add it
149
+ modules.push({
150
+ ...ManagerOptionsDefaults.defaultSessionsTopology,
151
+ imageHash: newImageHash,
152
+ });
153
+ }
154
+ else {
155
+ // Update the configuration to use the new session manager image hash
156
+ managerLeaf.imageHash = newImageHash;
157
+ }
158
+ return this.shared.modules.wallets.requestConfigurationUpdate(walletAddress, {
159
+ modules,
160
+ }, Actions.SessionUpdate, origin);
161
+ }
162
+ async completeSessionUpdate(requestId) {
163
+ const sigRequest = await this.shared.modules.signatures.get(requestId);
164
+ if (sigRequest.action !== 'session-update' || !Payload.isConfigUpdate(sigRequest.envelope.payload)) {
165
+ throw new Error('Invalid action');
166
+ }
167
+ return this.shared.modules.wallets.completeConfigurationUpdate(requestId);
168
+ }
169
+ }