@fedimint/core-web 0.0.2 → 0.0.4
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.
- package/LICENSE +21 -0
- package/README.md +34 -13
- package/dist/FedimintWallet.d.ts +40 -33
- package/dist/FedimintWallet.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/BalanceService.d.ts +8 -0
- package/dist/services/BalanceService.d.ts.map +1 -0
- package/dist/services/FederationService.d.ts +12 -0
- package/dist/services/FederationService.d.ts.map +1 -0
- package/dist/services/LightningService.d.ts +17 -0
- package/dist/services/LightningService.d.ts.map +1 -0
- package/dist/services/MintService.d.ts +15 -0
- package/dist/services/MintService.d.ts.map +1 -0
- package/dist/services/RecoveryService.d.ts +13 -0
- package/dist/services/RecoveryService.d.ts.map +1 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/types/wallet.d.ts +19 -3
- package/dist/types/wallet.d.ts.map +1 -1
- package/dist/worker/WorkerClient.d.ts +41 -0
- package/dist/worker/WorkerClient.d.ts.map +1 -0
- package/dist/worker/index.d.ts +2 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker.js +2 -0
- package/dist/worker.js.map +1 -0
- package/node_modules/@fedimint/fedimint-client-wasm/fedimint_client_wasm.d.ts +49 -0
- package/node_modules/@fedimint/fedimint-client-wasm/fedimint_client_wasm.js +4 -0
- package/node_modules/@fedimint/fedimint-client-wasm/fedimint_client_wasm_bg.js +1411 -0
- package/{wasm → node_modules/@fedimint/fedimint-client-wasm}/fedimint_client_wasm_bg.wasm +0 -0
- package/node_modules/@fedimint/fedimint-client-wasm/package.json +23 -0
- package/package.json +12 -5
- package/src/FedimintWallet.test.ts +74 -0
- package/src/FedimintWallet.ts +88 -342
- package/src/services/BalanceService.ts +24 -0
- package/src/services/FederationService.ts +32 -0
- package/src/services/LightningService.ts +128 -0
- package/src/services/MintService.ts +93 -0
- package/src/services/RecoveryService.ts +26 -0
- package/src/services/index.ts +5 -0
- package/src/types/wallet.ts +25 -2
- package/src/worker/WorkerClient.ts +190 -0
- package/src/worker/index.ts +1 -0
- package/src/worker/worker.js +85 -0
- package/dist/assets/fedimint_client_wasm_bg-DxOUItb-.wasm +0 -0
- package/dist/wasm.worker.d.ts +0 -1
- package/dist/wasm.worker.d.ts.map +0 -1
- package/src/vite-env.d.ts +0 -1
- package/src/wasm.worker.ts +0 -41
- package/wasm/fedimint_client_wasm.d.ts +0 -104
- package/wasm/fedimint_client_wasm.js +0 -1120
- package/wasm/fedimint_client_wasm_bg.js +0 -873
- package/wasm/fedimint_client_wasm_bg.wasm.d.ts +0 -34
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { WorkerClient } from '../worker'
|
|
2
|
+
import {
|
|
3
|
+
CreateBolt11Response,
|
|
4
|
+
GatewayInfo,
|
|
5
|
+
JSONObject,
|
|
6
|
+
JSONValue,
|
|
7
|
+
LightningGateway,
|
|
8
|
+
LnPayState,
|
|
9
|
+
LnReceiveState,
|
|
10
|
+
OutgoingLightningPayment,
|
|
11
|
+
} from '../types/wallet'
|
|
12
|
+
|
|
13
|
+
export class LightningService {
|
|
14
|
+
constructor(private client: WorkerClient) {}
|
|
15
|
+
|
|
16
|
+
async createBolt11InvoiceWithGateway(
|
|
17
|
+
amount: number,
|
|
18
|
+
description: string,
|
|
19
|
+
expiryTime: number | null = null,
|
|
20
|
+
extraMeta: JSONObject = {},
|
|
21
|
+
gatewayInfo: GatewayInfo,
|
|
22
|
+
) {
|
|
23
|
+
return await this.client.rpcSingle('ln', 'create_bolt11_invoice', {
|
|
24
|
+
amount,
|
|
25
|
+
description,
|
|
26
|
+
expiry_time: expiryTime,
|
|
27
|
+
extra_meta: extraMeta,
|
|
28
|
+
gateway: gatewayInfo,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async createBolt11Invoice(
|
|
33
|
+
amount: number,
|
|
34
|
+
description: string,
|
|
35
|
+
expiryTime: number | null = null,
|
|
36
|
+
extraMeta: JSONObject = {},
|
|
37
|
+
): Promise<CreateBolt11Response> {
|
|
38
|
+
await this.updateGatewayCache()
|
|
39
|
+
const gateway = await this._getDefaultGatewayInfo()
|
|
40
|
+
return await this.client.rpcSingle('ln', 'create_bolt11_invoice', {
|
|
41
|
+
amount,
|
|
42
|
+
description,
|
|
43
|
+
expiry_time: expiryTime,
|
|
44
|
+
extra_meta: extraMeta,
|
|
45
|
+
gateway: gateway.info,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async payBolt11InvoiceWithGateway(
|
|
50
|
+
invoice: string,
|
|
51
|
+
gatewayInfo: GatewayInfo,
|
|
52
|
+
extraMeta: JSONObject = {},
|
|
53
|
+
) {
|
|
54
|
+
return await this.client.rpcSingle('ln', 'pay_bolt11_invoice', {
|
|
55
|
+
maybe_gateway: gatewayInfo,
|
|
56
|
+
invoice,
|
|
57
|
+
extra_meta: extraMeta,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async _getDefaultGatewayInfo(): Promise<LightningGateway> {
|
|
62
|
+
const gateways = await this.listGateways()
|
|
63
|
+
return gateways[0]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async payBolt11Invoice(
|
|
67
|
+
invoice: string,
|
|
68
|
+
extraMeta: JSONObject = {},
|
|
69
|
+
): Promise<OutgoingLightningPayment> {
|
|
70
|
+
await this.updateGatewayCache()
|
|
71
|
+
const gateway = await this._getDefaultGatewayInfo()
|
|
72
|
+
return await this.client.rpcSingle('ln', 'pay_bolt11_invoice', {
|
|
73
|
+
maybe_gateway: gateway.info,
|
|
74
|
+
invoice,
|
|
75
|
+
extra_meta: extraMeta,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
subscribeLnPay(
|
|
80
|
+
operationId: string,
|
|
81
|
+
onSuccess: (state: LnPayState) => void = () => {},
|
|
82
|
+
onError: (error: string) => void = () => {},
|
|
83
|
+
) {
|
|
84
|
+
const unsubscribe = this.client.rpcStream(
|
|
85
|
+
'ln',
|
|
86
|
+
'subscribe_ln_pay',
|
|
87
|
+
{ operation_id: operationId },
|
|
88
|
+
onSuccess,
|
|
89
|
+
onError,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return unsubscribe
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
subscribeLnReceive(
|
|
96
|
+
operationId: string,
|
|
97
|
+
onSuccess: (state: LnReceiveState) => void = () => {},
|
|
98
|
+
onError: (error: string) => void = () => {},
|
|
99
|
+
) {
|
|
100
|
+
const unsubscribe = this.client.rpcStream(
|
|
101
|
+
'ln',
|
|
102
|
+
'subscribe_ln_receive',
|
|
103
|
+
{ operation_id: operationId },
|
|
104
|
+
onSuccess,
|
|
105
|
+
onError,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return unsubscribe
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getGateway(
|
|
112
|
+
gatewayId: string | null = null,
|
|
113
|
+
forceInternal: boolean = false,
|
|
114
|
+
): Promise<LightningGateway | null> {
|
|
115
|
+
return await this.client.rpcSingle('ln', 'get_gateway', {
|
|
116
|
+
gateway_id: gatewayId,
|
|
117
|
+
force_internal: forceInternal,
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async listGateways(): Promise<LightningGateway[]> {
|
|
122
|
+
return await this.client.rpcSingle('ln', 'list_gateways', {})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async updateGatewayCache(): Promise<JSONValue> {
|
|
126
|
+
return await this.client.rpcSingle('ln', 'update_gateway_cache', {})
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { WorkerClient } from '../worker'
|
|
2
|
+
import { JSONObject, JSONValue } from '../types/wallet'
|
|
3
|
+
|
|
4
|
+
export class MintService {
|
|
5
|
+
constructor(private client: WorkerClient) {}
|
|
6
|
+
|
|
7
|
+
async redeemEcash(notes: string): Promise<void> {
|
|
8
|
+
await this.client.rpcSingle('mint', 'reissue_external_notes', {
|
|
9
|
+
oob_notes: notes, // "out of band notes"
|
|
10
|
+
extra_meta: null,
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async reissueExternalNotes(
|
|
15
|
+
oobNotes: string,
|
|
16
|
+
extraMeta: JSONObject,
|
|
17
|
+
): Promise<string> {
|
|
18
|
+
return await this.client.rpcSingle('mint', 'reissue_external_notes', {
|
|
19
|
+
oob_notes: oobNotes,
|
|
20
|
+
extra_meta: extraMeta,
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
subscribeReissueExternalNotes(
|
|
25
|
+
operationId: string,
|
|
26
|
+
onSuccess: (state: JSONValue) => void = () => {},
|
|
27
|
+
onError: (error: string) => void = () => {},
|
|
28
|
+
) {
|
|
29
|
+
type ReissueExternalNotesState =
|
|
30
|
+
| 'Created'
|
|
31
|
+
| 'Issuing'
|
|
32
|
+
| 'Done'
|
|
33
|
+
| { Failed: { error: string } }
|
|
34
|
+
|
|
35
|
+
const unsubscribe = this.client.rpcStream<ReissueExternalNotesState>(
|
|
36
|
+
'mint',
|
|
37
|
+
'subscribe_reissue_external_notes',
|
|
38
|
+
{ operation_id: operationId },
|
|
39
|
+
onSuccess,
|
|
40
|
+
onError,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return unsubscribe
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async spendNotes(
|
|
47
|
+
minAmount: number,
|
|
48
|
+
tryCancelAfter: number,
|
|
49
|
+
includeInvite: boolean,
|
|
50
|
+
extraMeta: JSONValue,
|
|
51
|
+
): Promise<JSONValue> {
|
|
52
|
+
return await this.client.rpcSingle('mint', 'spend_notes', {
|
|
53
|
+
min_amount: minAmount,
|
|
54
|
+
try_cancel_after: tryCancelAfter,
|
|
55
|
+
include_invite: includeInvite,
|
|
56
|
+
extra_meta: extraMeta,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async validateNotes(oobNotes: string): Promise<number> {
|
|
61
|
+
return await this.client.rpcSingle('mint', 'validate_notes', {
|
|
62
|
+
oob_notes: oobNotes,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async tryCancelSpendNotes(operationId: string): Promise<void> {
|
|
67
|
+
await this.client.rpcSingle('mint', 'try_cancel_spend_notes', {
|
|
68
|
+
operation_id: operationId,
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
subscribeSpendNotes(
|
|
73
|
+
operationId: string,
|
|
74
|
+
onSuccess: (state: JSONValue) => void = () => {},
|
|
75
|
+
onError: (error: string) => void = () => {},
|
|
76
|
+
) {
|
|
77
|
+
const unsubscribe = this.client.rpcStream(
|
|
78
|
+
'mint',
|
|
79
|
+
'subscribe_spend_notes',
|
|
80
|
+
{ operation_id: operationId },
|
|
81
|
+
(res) => onSuccess(res),
|
|
82
|
+
onError,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return unsubscribe
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async awaitSpendOobRefund(operationId: string): Promise<JSONValue> {
|
|
89
|
+
return await this.client.rpcSingle('mint', 'await_spend_oob_refund', {
|
|
90
|
+
operation_id: operationId,
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { JSONValue } from '../types/wallet'
|
|
2
|
+
import { WorkerClient } from '../worker'
|
|
3
|
+
|
|
4
|
+
export class RecoveryService {
|
|
5
|
+
constructor(private client: WorkerClient) {}
|
|
6
|
+
|
|
7
|
+
async hasPendingRecoveries(): Promise<boolean> {
|
|
8
|
+
return await this.client.rpcSingle('', 'has_pending_recoveries', {})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async waitForAllRecoveries(): Promise<void> {
|
|
12
|
+
await this.client.rpcSingle('', 'wait_for_all_recoveries', {})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
subscribeToRecoveryProgress(
|
|
16
|
+
onSuccess: (progress: { module_id: number; progress: JSONValue }) => void,
|
|
17
|
+
onError: (error: string) => void,
|
|
18
|
+
): () => void {
|
|
19
|
+
const unsubscribe = this.client.rpcStream<{
|
|
20
|
+
module_id: number
|
|
21
|
+
progress: JSONValue
|
|
22
|
+
}>('', 'subscribe_to_recovery_progress', {}, onSuccess, onError)
|
|
23
|
+
|
|
24
|
+
return unsubscribe
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/types/wallet.ts
CHANGED
|
@@ -10,7 +10,7 @@ type JSONValue =
|
|
|
10
10
|
|
|
11
11
|
type JSONObject = Record<string, JSONValue>
|
|
12
12
|
|
|
13
|
-
type
|
|
13
|
+
type GatewayInfo = {
|
|
14
14
|
gateway_id: string
|
|
15
15
|
api: string
|
|
16
16
|
node_pub_key: string
|
|
@@ -18,6 +18,14 @@ type LightningGateway = {
|
|
|
18
18
|
route_hints: RouteHint[]
|
|
19
19
|
fees: FeeToAmount
|
|
20
20
|
}
|
|
21
|
+
type LightningGateway = {
|
|
22
|
+
info: GatewayInfo
|
|
23
|
+
vetted: boolean
|
|
24
|
+
ttl: {
|
|
25
|
+
nanos: number
|
|
26
|
+
secs: number
|
|
27
|
+
}
|
|
28
|
+
}
|
|
21
29
|
|
|
22
30
|
type RouteHint = {
|
|
23
31
|
// TODO: Define the structure of RouteHint
|
|
@@ -64,14 +72,27 @@ type CreateBolt11Response = {
|
|
|
64
72
|
type StreamError = {
|
|
65
73
|
error: string
|
|
66
74
|
data: never
|
|
75
|
+
end: never
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
type StreamSuccess<T extends JSONValue> = {
|
|
70
79
|
data: T
|
|
71
80
|
error: never
|
|
81
|
+
end: never
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type StreamEnd = {
|
|
85
|
+
end: string
|
|
86
|
+
data: never
|
|
87
|
+
error: never
|
|
72
88
|
}
|
|
73
89
|
|
|
74
|
-
type StreamResult<T extends JSONValue> =
|
|
90
|
+
type StreamResult<T extends JSONValue> =
|
|
91
|
+
| StreamSuccess<T>
|
|
92
|
+
| StreamError
|
|
93
|
+
| StreamEnd
|
|
94
|
+
|
|
95
|
+
type CancelFunction = () => void
|
|
75
96
|
|
|
76
97
|
export {
|
|
77
98
|
JSONValue,
|
|
@@ -84,8 +105,10 @@ export {
|
|
|
84
105
|
LnPayState,
|
|
85
106
|
LnReceiveState,
|
|
86
107
|
CreateBolt11Response,
|
|
108
|
+
GatewayInfo,
|
|
87
109
|
StreamError,
|
|
88
110
|
StreamSuccess,
|
|
89
111
|
StreamResult,
|
|
90
112
|
ModuleKind,
|
|
113
|
+
CancelFunction,
|
|
91
114
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CancelFunction,
|
|
3
|
+
JSONValue,
|
|
4
|
+
ModuleKind,
|
|
5
|
+
StreamError,
|
|
6
|
+
StreamResult,
|
|
7
|
+
} from '../types/wallet'
|
|
8
|
+
|
|
9
|
+
// Handles communication with the wasm worker
|
|
10
|
+
// TODO: Move rpc stream management to a separate "SubscriptionManager" class
|
|
11
|
+
export class WorkerClient {
|
|
12
|
+
private worker: Worker
|
|
13
|
+
private requestCounter = 0
|
|
14
|
+
private requestCallbacks = new Map<number, (value: any) => void>()
|
|
15
|
+
private initPromise: Promise<void> | null = null
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
// Must create the URL inside the constructor for vite
|
|
19
|
+
this.worker = new Worker(new URL('./worker.js', import.meta.url), {
|
|
20
|
+
type: 'module',
|
|
21
|
+
})
|
|
22
|
+
this.worker.onmessage = this.handleWorkerMessage.bind(this)
|
|
23
|
+
this.worker.onerror = this.handleWorkerError.bind(this)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Idempotent setup - Loads the wasm module
|
|
27
|
+
initialize() {
|
|
28
|
+
if (this.initPromise) return this.initPromise
|
|
29
|
+
this.initPromise = this.sendSingleMessage('init')
|
|
30
|
+
return this.initPromise
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private handleWorkerError(event: ErrorEvent) {
|
|
34
|
+
console.error('Worker error', JSON.stringify(event))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private handleWorkerMessage(event: MessageEvent) {
|
|
38
|
+
const { type, requestId, ...data } = event.data
|
|
39
|
+
const streamCallback = this.requestCallbacks.get(requestId)
|
|
40
|
+
// TODO: Handle errors... maybe have another callbacks list for errors?
|
|
41
|
+
if (streamCallback) {
|
|
42
|
+
streamCallback(data) // {data: something} OR {error: something}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// TODO: Handle errors... maybe have another callbacks list for errors?
|
|
47
|
+
// TODO: Handle timeouts
|
|
48
|
+
// TODO: Handle multiple errors
|
|
49
|
+
|
|
50
|
+
sendSingleMessage(type: string, payload?: any): Promise<any> {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const requestId = ++this.requestCounter
|
|
53
|
+
this.requestCallbacks.set(requestId, (response) => {
|
|
54
|
+
this.requestCallbacks.delete(requestId)
|
|
55
|
+
if (response.data) resolve(response.data)
|
|
56
|
+
else if (response.error) reject(response.error)
|
|
57
|
+
})
|
|
58
|
+
this.worker.postMessage({ type, payload, requestId })
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @summary Initiates an RPC stream with the specified module and method.
|
|
64
|
+
*
|
|
65
|
+
* @description
|
|
66
|
+
* This function sets up an RPC stream by sending a request to a worker and
|
|
67
|
+
* handling responses asynchronously. It ensures that unsubscription is handled
|
|
68
|
+
* correctly, even if the unsubscribe function is called before the subscription
|
|
69
|
+
* is fully established, by deferring the unsubscription attempt using `setTimeout`.
|
|
70
|
+
*
|
|
71
|
+
* The function operates in a non-blocking manner, leveraging Promises to manage
|
|
72
|
+
* asynchronous operations and callbacks to handle responses.
|
|
73
|
+
*
|
|
74
|
+
*
|
|
75
|
+
* @template Response - The expected type of the successful response.
|
|
76
|
+
* @template Body - The type of the request body.
|
|
77
|
+
* @param module - The module kind to interact with.
|
|
78
|
+
* @param method - The method name to invoke on the module.
|
|
79
|
+
* @param body - The request payload.
|
|
80
|
+
* @param onSuccess - Callback invoked with the response data on success.
|
|
81
|
+
* @param onError - Callback invoked with error information if an error occurs.
|
|
82
|
+
* @param onEnd - Optional callback invoked when the stream ends.
|
|
83
|
+
* @returns A function that can be called to cancel the subscription.
|
|
84
|
+
*
|
|
85
|
+
*/
|
|
86
|
+
rpcStream<
|
|
87
|
+
Response extends JSONValue = JSONValue,
|
|
88
|
+
Body extends JSONValue = JSONValue,
|
|
89
|
+
>(
|
|
90
|
+
module: ModuleKind,
|
|
91
|
+
method: string,
|
|
92
|
+
body: Body,
|
|
93
|
+
onSuccess: (res: Response) => void,
|
|
94
|
+
onError: (res: StreamError['error']) => void,
|
|
95
|
+
onEnd: () => void = () => {},
|
|
96
|
+
): CancelFunction {
|
|
97
|
+
const requestId = ++this.requestCounter
|
|
98
|
+
|
|
99
|
+
let unsubscribe: (value: void) => void = () => {}
|
|
100
|
+
let isSubscribed = false
|
|
101
|
+
|
|
102
|
+
const unsubscribePromise = new Promise<void>((resolve) => {
|
|
103
|
+
unsubscribe = () => {
|
|
104
|
+
if (isSubscribed) {
|
|
105
|
+
// If already subscribed, resolve immediately to trigger unsubscription
|
|
106
|
+
resolve()
|
|
107
|
+
} else {
|
|
108
|
+
// If not yet subscribed, defer the unsubscribe attempt to the next event loop tick
|
|
109
|
+
// This ensures that subscription setup has time to complete
|
|
110
|
+
setTimeout(() => unsubscribe(), 0)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// Initiate the inner RPC stream setup asynchronously
|
|
116
|
+
this._rpcStreamInner(
|
|
117
|
+
requestId,
|
|
118
|
+
module,
|
|
119
|
+
method,
|
|
120
|
+
body,
|
|
121
|
+
onSuccess,
|
|
122
|
+
onError,
|
|
123
|
+
onEnd,
|
|
124
|
+
unsubscribePromise,
|
|
125
|
+
).then(() => {
|
|
126
|
+
isSubscribed = true
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return unsubscribe
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private async _rpcStreamInner<
|
|
133
|
+
Response extends JSONValue = JSONValue,
|
|
134
|
+
Body extends JSONValue = JSONValue,
|
|
135
|
+
>(
|
|
136
|
+
requestId: number,
|
|
137
|
+
module: ModuleKind,
|
|
138
|
+
method: string,
|
|
139
|
+
body: Body,
|
|
140
|
+
onSuccess: (res: Response) => void,
|
|
141
|
+
onError: (res: StreamError['error']) => void,
|
|
142
|
+
onEnd: () => void = () => {},
|
|
143
|
+
unsubscribePromise: Promise<void>,
|
|
144
|
+
// Unsubscribe function
|
|
145
|
+
): Promise<void> {
|
|
146
|
+
// await this.openPromise
|
|
147
|
+
// if (!this.worker || !this._isOpen)
|
|
148
|
+
// throw new Error('FedimintWallet is not open')
|
|
149
|
+
|
|
150
|
+
this.requestCallbacks.set(requestId, (response: StreamResult<Response>) => {
|
|
151
|
+
if (response.error !== undefined) {
|
|
152
|
+
onError(response.error)
|
|
153
|
+
} else if (response.data !== undefined) {
|
|
154
|
+
onSuccess(response.data)
|
|
155
|
+
} else if (response.end !== undefined) {
|
|
156
|
+
this.requestCallbacks.delete(requestId)
|
|
157
|
+
onEnd()
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
this.worker.postMessage({
|
|
161
|
+
type: 'rpc',
|
|
162
|
+
payload: { module, method, body },
|
|
163
|
+
requestId,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
unsubscribePromise.then(() => {
|
|
167
|
+
this.worker?.postMessage({
|
|
168
|
+
type: 'unsubscribe',
|
|
169
|
+
requestId,
|
|
170
|
+
})
|
|
171
|
+
this.requestCallbacks.delete(requestId)
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
rpcSingle<Response extends JSONValue = JSONValue>(
|
|
176
|
+
module: ModuleKind,
|
|
177
|
+
method: string,
|
|
178
|
+
body: JSONValue,
|
|
179
|
+
): Promise<Response> {
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
this.rpcStream<Response>(module, method, body, resolve, reject)
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
cleanup() {
|
|
186
|
+
this.worker.terminate()
|
|
187
|
+
this.initPromise = null
|
|
188
|
+
this.requestCallbacks.clear()
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WorkerClient } from './WorkerClient'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Web Worker for fedimint-client-wasm to run in the browser
|
|
2
|
+
|
|
3
|
+
// HACK: Fixes vitest browser runner
|
|
4
|
+
globalThis.__vitest_browser_runner__ = { wrapDynamicImport: (foo) => foo() }
|
|
5
|
+
|
|
6
|
+
let WasmClient = null
|
|
7
|
+
let client = null
|
|
8
|
+
|
|
9
|
+
const streamCancelMap = new Map()
|
|
10
|
+
|
|
11
|
+
const handleFree = (requestId) => {
|
|
12
|
+
streamCancelMap.delete(requestId)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
self.onmessage = async (event) => {
|
|
16
|
+
const { type, payload, requestId } = event.data
|
|
17
|
+
|
|
18
|
+
if (type === 'init') {
|
|
19
|
+
WasmClient = (await import('@fedimint/fedimint-client-wasm')).WasmClient
|
|
20
|
+
self.postMessage({ type: 'initialized', data: {}, requestId })
|
|
21
|
+
} else if (type === 'open') {
|
|
22
|
+
const { clientName } = payload
|
|
23
|
+
client = (await WasmClient.open(clientName)) || null
|
|
24
|
+
self.postMessage({
|
|
25
|
+
type: 'open',
|
|
26
|
+
data: { success: !!client },
|
|
27
|
+
requestId,
|
|
28
|
+
})
|
|
29
|
+
} else if (type === 'join') {
|
|
30
|
+
const { inviteCode, clientName: joinClientName } = payload
|
|
31
|
+
try {
|
|
32
|
+
client = await WasmClient.join_federation(joinClientName, inviteCode)
|
|
33
|
+
self.postMessage({
|
|
34
|
+
type: 'join',
|
|
35
|
+
data: { success: !!client },
|
|
36
|
+
requestId,
|
|
37
|
+
})
|
|
38
|
+
} catch (e) {
|
|
39
|
+
self.postMessage({ type: 'error', error: e.message, requestId })
|
|
40
|
+
}
|
|
41
|
+
} else if (type === 'rpc') {
|
|
42
|
+
const { module, method, body } = payload
|
|
43
|
+
console.log('RPC received', module, method, body)
|
|
44
|
+
if (!client) {
|
|
45
|
+
self.postMessage({
|
|
46
|
+
type: 'error',
|
|
47
|
+
error: 'WasmClient not initialized',
|
|
48
|
+
requestId,
|
|
49
|
+
})
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
const rpcHandle = await client.rpc(
|
|
53
|
+
module,
|
|
54
|
+
method,
|
|
55
|
+
JSON.stringify(body),
|
|
56
|
+
(res) => {
|
|
57
|
+
console.log('RPC response', requestId, res)
|
|
58
|
+
const data = JSON.parse(res)
|
|
59
|
+
self.postMessage({ type: 'rpcResponse', requestId, ...data })
|
|
60
|
+
|
|
61
|
+
if (data.end !== undefined) {
|
|
62
|
+
// Handle stream ending
|
|
63
|
+
const handle = streamCancelMap.get(requestId)
|
|
64
|
+
handle?.free()
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
streamCancelMap.set(requestId, rpcHandle)
|
|
69
|
+
} else if (type === 'unsubscribe') {
|
|
70
|
+
const rpcHandle = streamCancelMap.get(requestId)
|
|
71
|
+
if (rpcHandle) {
|
|
72
|
+
rpcHandle.cancel()
|
|
73
|
+
rpcHandle.free()
|
|
74
|
+
streamCancelMap.delete(requestId)
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
self.postMessage({
|
|
78
|
+
type: 'error',
|
|
79
|
+
error: 'Unknown message type',
|
|
80
|
+
requestId,
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
self.postMessage({ type: 'init', data: {} })
|
|
Binary file
|
package/dist/wasm.worker.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=wasm.worker.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wasm.worker.d.ts","sourceRoot":"","sources":["../src/wasm.worker.ts"],"names":[],"mappings":""}
|
package/src/vite-env.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|
package/src/wasm.worker.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// import init, { InitOutput, WasmClient } from '../wasm/fedimint_client_wasm.js'
|
|
2
|
-
|
|
3
|
-
// let client: WasmClient | undefined
|
|
4
|
-
|
|
5
|
-
// async function workerInit() {
|
|
6
|
-
// await init(new URL('@fedi/common/wasm/fedi_wasm_bg.wasm', import.meta.url))
|
|
7
|
-
// }
|
|
8
|
-
|
|
9
|
-
// const initPromise = workerInit().catch((error) =>
|
|
10
|
-
// postMessage({ error: String(error) }),
|
|
11
|
-
// )
|
|
12
|
-
|
|
13
|
-
// async function rpcRequest(method: string, data: string): Promise<string> {
|
|
14
|
-
// await initPromise
|
|
15
|
-
// return await fedimint_rpc(method, data)
|
|
16
|
-
// }
|
|
17
|
-
|
|
18
|
-
// const handleMessage = async (token: string, method: string, data: string) => {
|
|
19
|
-
// if (method === 'open') {
|
|
20
|
-
// await initPromise
|
|
21
|
-
// client = await WasmClient.open(data)
|
|
22
|
-
// if (client !== undefined) {
|
|
23
|
-
// postMessage({ token, result: true })
|
|
24
|
-
// } else {
|
|
25
|
-
// postMessage({ token, result: false })
|
|
26
|
-
// }
|
|
27
|
-
// } else if (method === 'join_federation') {
|
|
28
|
-
// await initPromise
|
|
29
|
-
// const result = await WasmClient.join_federation(data)
|
|
30
|
-
// postMessage({ token, result })
|
|
31
|
-
// }
|
|
32
|
-
// }
|
|
33
|
-
|
|
34
|
-
// // Handles worker.postMessage calls
|
|
35
|
-
// addEventListener('message', (e) => {
|
|
36
|
-
// const { token, method, data } = e.data
|
|
37
|
-
// handleMessage(token, method, data)
|
|
38
|
-
// // rpcRequest(method, data)
|
|
39
|
-
// // .then((result) => postMessage({ token, result }))
|
|
40
|
-
// // .catch((error) => postMessage({ error: String(error) }))
|
|
41
|
-
// })
|