@bsv/wallet-toolbox 1.7.22 → 1.8.1

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 (54) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/docs/README.md +1 -0
  3. package/docs/client.md +135 -0
  4. package/docs/wab-shamir.md +311 -0
  5. package/docs/wallet.md +135 -0
  6. package/out/src/ShamirWalletManager.d.ts +213 -0
  7. package/out/src/ShamirWalletManager.d.ts.map +1 -0
  8. package/out/src/ShamirWalletManager.js +363 -0
  9. package/out/src/ShamirWalletManager.js.map +1 -0
  10. package/out/src/WalletPermissionsManager.d.ts +27 -0
  11. package/out/src/WalletPermissionsManager.d.ts.map +1 -1
  12. package/out/src/WalletPermissionsManager.js +308 -147
  13. package/out/src/WalletPermissionsManager.js.map +1 -1
  14. package/out/src/__tests/ShamirWalletManager.test.d.ts +2 -0
  15. package/out/src/__tests/ShamirWalletManager.test.d.ts.map +1 -0
  16. package/out/src/__tests/ShamirWalletManager.test.js +298 -0
  17. package/out/src/__tests/ShamirWalletManager.test.js.map +1 -0
  18. package/out/src/__tests/WalletPermissionsManager.callbacks.test.js +116 -0
  19. package/out/src/__tests/WalletPermissionsManager.callbacks.test.js.map +1 -1
  20. package/out/src/__tests/WalletPermissionsManager.pmodules.test.js +111 -0
  21. package/out/src/__tests/WalletPermissionsManager.pmodules.test.js.map +1 -1
  22. package/out/src/entropy/EntropyCollector.d.ts +89 -0
  23. package/out/src/entropy/EntropyCollector.d.ts.map +1 -0
  24. package/out/src/entropy/EntropyCollector.js +176 -0
  25. package/out/src/entropy/EntropyCollector.js.map +1 -0
  26. package/out/src/entropy/__tests/EntropyCollector.test.d.ts +2 -0
  27. package/out/src/entropy/__tests/EntropyCollector.test.d.ts.map +1 -0
  28. package/out/src/entropy/__tests/EntropyCollector.test.js +137 -0
  29. package/out/src/entropy/__tests/EntropyCollector.test.js.map +1 -0
  30. package/out/src/index.all.d.ts +2 -0
  31. package/out/src/index.all.d.ts.map +1 -1
  32. package/out/src/index.all.js +2 -0
  33. package/out/src/index.all.js.map +1 -1
  34. package/out/src/sdk/WalletServices.interfaces.d.ts.map +1 -1
  35. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.d.ts.map +1 -1
  36. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js +4 -1
  37. package/out/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.js.map +1 -1
  38. package/out/src/wab-client/WABClient.d.ts +65 -0
  39. package/out/src/wab-client/WABClient.d.ts.map +1 -1
  40. package/out/src/wab-client/WABClient.js +107 -0
  41. package/out/src/wab-client/WABClient.js.map +1 -1
  42. package/out/tsconfig.all.tsbuildinfo +1 -1
  43. package/package.json +5 -1
  44. package/src/ShamirWalletManager.ts +499 -0
  45. package/src/WalletPermissionsManager.ts +368 -181
  46. package/src/__tests/ShamirWalletManager.test.ts +369 -0
  47. package/src/__tests/WalletPermissionsManager.callbacks.test.ts +140 -1
  48. package/src/__tests/WalletPermissionsManager.pmodules.test.ts +152 -0
  49. package/src/entropy/EntropyCollector.ts +228 -0
  50. package/src/entropy/__tests/EntropyCollector.test.ts +182 -0
  51. package/src/index.all.ts +2 -0
  52. package/src/sdk/WalletServices.interfaces.ts +0 -1
  53. package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.ts +4 -1
  54. package/src/wab-client/WABClient.ts +135 -0
@@ -0,0 +1,228 @@
1
+ /**
2
+ * EntropyCollector
3
+ *
4
+ * Collects entropy from user interactions (mouse movements) and mixes it with
5
+ * cryptographically secure random numbers to generate high-quality random seeds
6
+ * for key generation.
7
+ *
8
+ * This provides defense-in-depth: even if the system's CSPRNG is compromised,
9
+ * the user-provided entropy adds unpredictability. Conversely, if the user's
10
+ * mouse movements are predictable, the CSPRNG ensures security.
11
+ */
12
+
13
+ import { Random, Hash } from '@bsv/sdk'
14
+
15
+ export interface EntropyCollectorConfig {
16
+ /** Target number of mouse samples to collect (default: 256) */
17
+ targetSamples?: number
18
+ /** Minimum time between samples in ms (default: 10) */
19
+ minSampleInterval?: number
20
+ }
21
+
22
+ export interface EntropyProgress {
23
+ /** Number of samples collected so far */
24
+ collected: number
25
+ /** Target number of samples */
26
+ target: number
27
+ /** Percentage complete (0-100) */
28
+ percent: number
29
+ }
30
+
31
+ /**
32
+ * Callback type for entropy collection progress updates
33
+ */
34
+ export type EntropyProgressCallback = (progress: EntropyProgress) => void
35
+
36
+ /**
37
+ * Collected entropy data before mixing
38
+ */
39
+ interface RawEntropyData {
40
+ mousePositions: Array<{ x: number; y: number; time: number }>
41
+ timingDeltas: number[]
42
+ }
43
+
44
+ export class EntropyCollector {
45
+ private config: Required<EntropyCollectorConfig>
46
+ private rawEntropy: RawEntropyData = {
47
+ mousePositions: [],
48
+ timingDeltas: []
49
+ }
50
+ private lastSampleTime: number = 0
51
+
52
+ constructor(config: EntropyCollectorConfig = {}) {
53
+ this.config = {
54
+ targetSamples: config.targetSamples ?? 256,
55
+ minSampleInterval: config.minSampleInterval ?? 10
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Reset collected entropy data
61
+ */
62
+ reset(): void {
63
+ this.rawEntropy = {
64
+ mousePositions: [],
65
+ timingDeltas: []
66
+ }
67
+ this.lastSampleTime = 0
68
+ }
69
+
70
+ /**
71
+ * Add a mouse movement sample
72
+ * Call this from a mousemove event handler
73
+ *
74
+ * @returns Progress information, or null if sample was rejected (too soon)
75
+ */
76
+ addMouseSample(x: number, y: number): EntropyProgress | null {
77
+ const now = performance.now()
78
+
79
+ // Rate limit samples to reduce correlation
80
+ if (now - this.lastSampleTime < this.config.minSampleInterval) {
81
+ return null
82
+ }
83
+
84
+ // Record timing delta (high precision timing adds entropy)
85
+ if (this.lastSampleTime > 0) {
86
+ this.rawEntropy.timingDeltas.push(now - this.lastSampleTime)
87
+ }
88
+ this.lastSampleTime = now
89
+
90
+ // Record position
91
+ this.rawEntropy.mousePositions.push({ x, y, time: now })
92
+
93
+ return this.getProgress()
94
+ }
95
+
96
+ /**
97
+ * Get current collection progress
98
+ */
99
+ getProgress(): EntropyProgress {
100
+ const collected = this.rawEntropy.mousePositions.length
101
+ return {
102
+ collected,
103
+ target: this.config.targetSamples,
104
+ percent: Math.min(100, Math.floor((collected / this.config.targetSamples) * 100))
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Check if enough entropy has been collected
110
+ */
111
+ isComplete(): boolean {
112
+ return this.rawEntropy.mousePositions.length >= this.config.targetSamples
113
+ }
114
+
115
+ /**
116
+ * Extract entropy bytes from collected mouse data
117
+ * Uses SHA-256 to compress and whiten the data
118
+ */
119
+ private extractRawEntropy(): Uint8Array {
120
+ // Serialize all collected data
121
+ const dataPoints: number[] = []
122
+
123
+ // Add mouse positions
124
+ for (const pos of this.rawEntropy.mousePositions) {
125
+ // Use lower bits of coordinates (more random due to jitter)
126
+ dataPoints.push(pos.x & 0xff)
127
+ dataPoints.push(pos.y & 0xff)
128
+ // Include fractional timing (microsecond precision)
129
+ dataPoints.push(Math.floor(pos.time * 1000) & 0xff)
130
+ dataPoints.push(Math.floor(pos.time * 1000000) & 0xff)
131
+ }
132
+
133
+ // Add timing deltas (high entropy source)
134
+ for (const delta of this.rawEntropy.timingDeltas) {
135
+ // Timing deltas converted to bytes
136
+ dataPoints.push(Math.floor(delta * 1000) & 0xff)
137
+ dataPoints.push(Math.floor(delta * 1000000) & 0xff)
138
+ }
139
+
140
+ // Hash to compress and whiten the entropy
141
+ const hash = Hash.sha256(dataPoints)
142
+ return new Uint8Array(hash)
143
+ }
144
+
145
+ /**
146
+ * Mix user entropy with system CSPRNG for defense-in-depth
147
+ *
148
+ * @returns 32 bytes of high-quality random data suitable for key generation
149
+ */
150
+ mixWithCSPRNG(): Uint8Array {
151
+ const userEntropy = this.extractRawEntropy()
152
+ const systemEntropy = new Uint8Array(Random(32))
153
+
154
+ // XOR user entropy with system entropy
155
+ const mixed = new Uint8Array(32)
156
+ for (let i = 0; i < 32; i++) {
157
+ mixed[i] = userEntropy[i] ^ systemEntropy[i]
158
+ }
159
+
160
+ // Final hash to ensure uniform distribution
161
+ const final = Hash.sha256(Array.from(mixed))
162
+ return new Uint8Array(final)
163
+ }
164
+
165
+ /**
166
+ * Generate entropy with automatic CSPRNG fallback
167
+ *
168
+ * If not enough user entropy has been collected, supplements with CSPRNG.
169
+ * Always mixes with CSPRNG regardless.
170
+ *
171
+ * @returns 32 bytes suitable for private key generation
172
+ */
173
+ generateEntropy(): Uint8Array {
174
+ if (!this.isComplete()) {
175
+ console.warn(
176
+ `EntropyCollector: Only ${this.rawEntropy.mousePositions.length}/${this.config.targetSamples} ` +
177
+ `samples collected. Supplementing with additional CSPRNG entropy.`
178
+ )
179
+ }
180
+
181
+ return this.mixWithCSPRNG()
182
+ }
183
+
184
+ /**
185
+ * Convenience method for browser environments.
186
+ * Creates event listeners and resolves when enough entropy is collected.
187
+ *
188
+ * @param element The HTML element to listen on (default: document)
189
+ * @param onProgress Optional callback for progress updates
190
+ * @returns Promise that resolves with 32 bytes of entropy
191
+ */
192
+ async collectFromBrowser(element: EventTarget = document, onProgress?: EntropyProgressCallback): Promise<Uint8Array> {
193
+ return new Promise(resolve => {
194
+ const handler = (event: Event) => {
195
+ if (event instanceof MouseEvent) {
196
+ const progress = this.addMouseSample(event.clientX, event.clientY)
197
+ if (progress) {
198
+ onProgress?.(progress)
199
+
200
+ if (this.isComplete()) {
201
+ element.removeEventListener('mousemove', handler)
202
+ resolve(this.generateEntropy())
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ element.addEventListener('mousemove', handler)
209
+ })
210
+ }
211
+
212
+ /**
213
+ * Estimate the quality of collected entropy in bits
214
+ * This is a rough heuristic, not a cryptographic guarantee
215
+ */
216
+ estimateEntropyBits(): number {
217
+ const samples = this.rawEntropy.mousePositions.length
218
+
219
+ if (samples === 0) return 0
220
+
221
+ // Assume roughly 4-6 bits per mouse position (jitter in lower bits)
222
+ // Plus 2-4 bits from timing
223
+ const bitsPerSample = 6
224
+
225
+ // Cap at 256 bits (32 bytes) since that's our output size
226
+ return Math.min(256, samples * bitsPerSample)
227
+ }
228
+ }
@@ -0,0 +1,182 @@
1
+ import { EntropyCollector } from '../EntropyCollector'
2
+
3
+ describe('EntropyCollector', () => {
4
+ describe('Configuration', () => {
5
+ it('should use default configuration', () => {
6
+ const collector = new EntropyCollector()
7
+ const progress = collector.getProgress()
8
+
9
+ expect(progress.target).toBe(256)
10
+ expect(progress.collected).toBe(0)
11
+ expect(progress.percent).toBe(0)
12
+ })
13
+
14
+ it('should accept custom configuration', () => {
15
+ const collector = new EntropyCollector({
16
+ targetSamples: 100,
17
+ minSampleInterval: 20
18
+ })
19
+ const progress = collector.getProgress()
20
+
21
+ expect(progress.target).toBe(100)
22
+ })
23
+ })
24
+
25
+ describe('Sample Collection', () => {
26
+ it('should collect mouse samples', () => {
27
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 0 })
28
+
29
+ const progress = collector.addMouseSample(100, 200)
30
+
31
+ expect(progress).not.toBeNull()
32
+ expect(progress!.collected).toBe(1)
33
+ })
34
+
35
+ it('should reject samples that are too close together', () => {
36
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 1000 })
37
+
38
+ collector.addMouseSample(100, 200)
39
+ const secondProgress = collector.addMouseSample(101, 201)
40
+
41
+ expect(secondProgress).toBeNull()
42
+ })
43
+
44
+ it('should track progress percentage', () => {
45
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 0 })
46
+
47
+ for (let i = 0; i < 5; i++) {
48
+ collector.addMouseSample(i * 10, i * 20)
49
+ }
50
+
51
+ const progress = collector.getProgress()
52
+ expect(progress.percent).toBe(50)
53
+ })
54
+
55
+ it('should cap percent at 100', () => {
56
+ const collector = new EntropyCollector({ targetSamples: 5, minSampleInterval: 0 })
57
+
58
+ for (let i = 0; i < 10; i++) {
59
+ collector.addMouseSample(i * 10, i * 20)
60
+ }
61
+
62
+ const progress = collector.getProgress()
63
+ expect(progress.percent).toBe(100)
64
+ })
65
+ })
66
+
67
+ describe('Completion', () => {
68
+ it('should report incomplete when not enough samples', () => {
69
+ const collector = new EntropyCollector({ targetSamples: 100, minSampleInterval: 0 })
70
+
71
+ for (let i = 0; i < 50; i++) {
72
+ collector.addMouseSample(i, i)
73
+ }
74
+
75
+ expect(collector.isComplete()).toBe(false)
76
+ })
77
+
78
+ it('should report complete when enough samples collected', () => {
79
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 0 })
80
+
81
+ for (let i = 0; i < 10; i++) {
82
+ collector.addMouseSample(i * 5, i * 10)
83
+ }
84
+
85
+ expect(collector.isComplete()).toBe(true)
86
+ })
87
+ })
88
+
89
+ describe('Reset', () => {
90
+ it('should reset collected samples', () => {
91
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 0 })
92
+
93
+ for (let i = 0; i < 5; i++) {
94
+ collector.addMouseSample(i, i)
95
+ }
96
+
97
+ expect(collector.getProgress().collected).toBe(5)
98
+
99
+ collector.reset()
100
+
101
+ expect(collector.getProgress().collected).toBe(0)
102
+ expect(collector.isComplete()).toBe(false)
103
+ })
104
+ })
105
+
106
+ describe('Entropy Generation', () => {
107
+ it('should generate 32 bytes of entropy', () => {
108
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 0 })
109
+
110
+ for (let i = 0; i < 10; i++) {
111
+ collector.addMouseSample(Math.random() * 1000, Math.random() * 1000)
112
+ }
113
+
114
+ const entropy = collector.generateEntropy()
115
+
116
+ expect(entropy).toBeInstanceOf(Uint8Array)
117
+ expect(entropy.length).toBe(32)
118
+ })
119
+
120
+ it('should generate entropy even with insufficient samples (with warning)', () => {
121
+ const collector = new EntropyCollector({ targetSamples: 100, minSampleInterval: 0 })
122
+
123
+ // Only add a few samples
124
+ for (let i = 0; i < 5; i++) {
125
+ collector.addMouseSample(i, i)
126
+ }
127
+
128
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
129
+
130
+ const entropy = collector.generateEntropy()
131
+
132
+ expect(entropy.length).toBe(32)
133
+ expect(consoleSpy).toHaveBeenCalled()
134
+
135
+ consoleSpy.mockRestore()
136
+ })
137
+
138
+ it('should generate different entropy on each call (due to CSPRNG mixing)', () => {
139
+ const collector = new EntropyCollector({ targetSamples: 10, minSampleInterval: 0 })
140
+
141
+ for (let i = 0; i < 10; i++) {
142
+ collector.addMouseSample(i, i)
143
+ }
144
+
145
+ const entropy1 = collector.generateEntropy()
146
+ const entropy2 = collector.generateEntropy()
147
+
148
+ // They should be different due to CSPRNG mixing
149
+ expect(Buffer.from(entropy1).toString('hex')).not.toBe(Buffer.from(entropy2).toString('hex'))
150
+ })
151
+ })
152
+
153
+ describe('Entropy Estimation', () => {
154
+ it('should estimate 0 bits with no samples', () => {
155
+ const collector = new EntropyCollector()
156
+ expect(collector.estimateEntropyBits()).toBe(0)
157
+ })
158
+
159
+ it('should estimate entropy based on sample count', () => {
160
+ const collector = new EntropyCollector({ targetSamples: 100, minSampleInterval: 0 })
161
+
162
+ for (let i = 0; i < 20; i++) {
163
+ collector.addMouseSample(i, i)
164
+ }
165
+
166
+ const bits = collector.estimateEntropyBits()
167
+ expect(bits).toBeGreaterThan(0)
168
+ expect(bits).toBeLessThanOrEqual(256) // Capped at 256
169
+ })
170
+
171
+ it('should cap entropy estimate at 256 bits', () => {
172
+ const collector = new EntropyCollector({ targetSamples: 1000, minSampleInterval: 0 })
173
+
174
+ for (let i = 0; i < 500; i++) {
175
+ collector.addMouseSample(i, i)
176
+ }
177
+
178
+ const bits = collector.estimateEntropyBits()
179
+ expect(bits).toBe(256)
180
+ })
181
+ })
182
+ })
package/src/index.all.ts CHANGED
@@ -25,3 +25,5 @@ export * from './Wallet'
25
25
  export * from './WalletLogger'
26
26
  export * from './WalletAuthenticationManager'
27
27
  export * from './WalletPermissionsManager'
28
+ export * from './ShamirWalletManager'
29
+ export * from './entropy/EntropyCollector'
@@ -9,7 +9,6 @@ import {
9
9
  import { Chain, ReqHistoryNote } from './types'
10
10
  import { WalletError } from './WalletError'
11
11
  import { TableOutput } from '../storage/schema/tables/TableOutput'
12
- import { ChaintracksServiceClient } from '../services/chaintracker/chaintracks/ChaintracksServiceClient'
13
12
  import { ChaintracksClientApi } from '../services/chaintracker/chaintracks/Api/ChaintracksClientApi'
14
13
  /**
15
14
  * Defines standard interfaces to access functionality implemented by external transaction processing services.
@@ -365,7 +365,10 @@ export abstract class ChaintracksStorageBase implements ChaintracksStorageQueryA
365
365
  private async addLiveHeadersToBulk(liveHeaders: LiveBlockHeader[]) {
366
366
  if (liveHeaders.length === 0) return
367
367
  const lastChainWork = liveHeaders.slice(-1)[0].chainWork
368
- await this.bulkManager.mergeIncrementalBlockHeaders(liveHeaders, lastChainWork)
368
+ const firstHeader = liveHeaders[0]
369
+ const previousWork = subWork(firstHeader.chainWork, convertBitsToWork(liveHeaders[0].bits))
370
+ const incrementalWork = subWork(lastChainWork, previousWork)
371
+ await this.bulkManager.mergeIncrementalBlockHeaders(liveHeaders, incrementalWork)
369
372
  }
370
373
  }
371
374
 
@@ -91,4 +91,139 @@ export class WABClient {
91
91
  })
92
92
  return res.json()
93
93
  }
94
+
95
+ // ============================================================
96
+ // Shamir Share Management (2-of-3 Key Recovery System)
97
+ // ============================================================
98
+
99
+ /**
100
+ * Start OTP verification for share operations
101
+ * This initiates the auth flow (e.g., sends SMS code via Twilio)
102
+ *
103
+ * @param methodType The auth method type (e.g., "TwilioPhone", "DevConsole")
104
+ * @param userIdHash SHA256 hash of the user's identity key
105
+ * @param payload Auth method specific data (e.g., { phoneNumber: "+1..." })
106
+ */
107
+ public async startShareAuth(
108
+ methodType: string,
109
+ userIdHash: string,
110
+ payload: any
111
+ ): Promise<{ success: boolean; message: string }> {
112
+ const res = await fetch(`${this.serverUrl}/auth/start`, {
113
+ method: 'POST',
114
+ headers: { 'Content-Type': 'application/json' },
115
+ body: JSON.stringify({
116
+ methodType,
117
+ presentationKey: userIdHash, // Reuse existing auth flow with userIdHash
118
+ payload
119
+ })
120
+ })
121
+ return res.json()
122
+ }
123
+
124
+ /**
125
+ * Store a Shamir share (Share B) on the server
126
+ * Requires prior OTP verification via startShareAuth
127
+ *
128
+ * @param methodType The auth method type used for verification
129
+ * @param payload Contains the OTP code and auth method specific data
130
+ * @param shareB The Shamir share to store (format: x.y.threshold.integrity)
131
+ * @param userIdHash SHA256 hash of the user's identity key
132
+ */
133
+ public async storeShare(
134
+ methodType: string,
135
+ payload: any,
136
+ shareB: string,
137
+ userIdHash: string
138
+ ): Promise<{ success: boolean; message: string; userId?: number }> {
139
+ const res = await fetch(`${this.serverUrl}/share/store`, {
140
+ method: 'POST',
141
+ headers: { 'Content-Type': 'application/json' },
142
+ body: JSON.stringify({
143
+ methodType,
144
+ payload,
145
+ shareB,
146
+ userIdHash
147
+ })
148
+ })
149
+ return res.json()
150
+ }
151
+
152
+ /**
153
+ * Retrieve a Shamir share (Share B) from the server
154
+ * Requires OTP verification
155
+ *
156
+ * @param methodType The auth method type used for verification
157
+ * @param payload Contains the OTP code and auth method specific data
158
+ * @param userIdHash SHA256 hash of the user's identity key
159
+ */
160
+ public async retrieveShare(
161
+ methodType: string,
162
+ payload: any,
163
+ userIdHash: string
164
+ ): Promise<{ success: boolean; shareB?: string; message: string }> {
165
+ const res = await fetch(`${this.serverUrl}/share/retrieve`, {
166
+ method: 'POST',
167
+ headers: { 'Content-Type': 'application/json' },
168
+ body: JSON.stringify({
169
+ methodType,
170
+ payload,
171
+ userIdHash
172
+ })
173
+ })
174
+ return res.json()
175
+ }
176
+
177
+ /**
178
+ * Update a Shamir share (for key rotation)
179
+ * Requires OTP verification
180
+ *
181
+ * @param methodType The auth method type used for verification
182
+ * @param payload Contains the OTP code and auth method specific data
183
+ * @param userIdHash SHA256 hash of the user's identity key
184
+ * @param newShareB The new Shamir share to store
185
+ */
186
+ public async updateShare(
187
+ methodType: string,
188
+ payload: any,
189
+ userIdHash: string,
190
+ newShareB: string
191
+ ): Promise<{ success: boolean; message: string; shareVersion?: number }> {
192
+ const res = await fetch(`${this.serverUrl}/share/update`, {
193
+ method: 'POST',
194
+ headers: { 'Content-Type': 'application/json' },
195
+ body: JSON.stringify({
196
+ methodType,
197
+ payload,
198
+ userIdHash,
199
+ newShareB
200
+ })
201
+ })
202
+ return res.json()
203
+ }
204
+
205
+ /**
206
+ * Delete a Shamir user's account and stored share
207
+ * Requires OTP verification
208
+ *
209
+ * @param methodType The auth method type used for verification
210
+ * @param payload Contains the OTP code and auth method specific data
211
+ * @param userIdHash SHA256 hash of the user's identity key
212
+ */
213
+ public async deleteShamirUser(
214
+ methodType: string,
215
+ payload: any,
216
+ userIdHash: string
217
+ ): Promise<{ success: boolean; message: string }> {
218
+ const res = await fetch(`${this.serverUrl}/share/delete`, {
219
+ method: 'POST',
220
+ headers: { 'Content-Type': 'application/json' },
221
+ body: JSON.stringify({
222
+ methodType,
223
+ payload,
224
+ userIdHash
225
+ })
226
+ })
227
+ return res.json()
228
+ }
94
229
  }