@fedimint/core-web 0.0.0-canary-20240910231647

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/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # @fedimint/core-web
2
+
3
+ ### THIS IS A WORK IN PROGRESS AND NOT READY FOR USE
4
+
5
+ This package provides a typescript interface for the Fedimint client in the browser
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pnpm add @fedimint/core-web
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { FedimintWallet } from '@fedimint/core-web'
17
+
18
+ // federation invite code
19
+ const inviteCode = 'fed11qgqpw9thwvaz7t...'
20
+
21
+ // This should be called only once
22
+ await FedimintWallet.initFedimint()
23
+
24
+ const wallet = await FedimintWallet.joinFederation(inviteCode)
25
+
26
+ // Get Wallet Balance
27
+ const balance = await wallet.getBalance()
28
+
29
+ // Receive Ecash Payments
30
+ await wallet.reissueNotes('A11qgqpw9thwvaz7t...')
31
+
32
+ // Pay Lightning Invoice
33
+ await wallet.payInvoice('lnbc...')
34
+ ```
35
+
36
+ ## Check out the example
37
+
38
+ [`examples/vite-core`](../examples/vite-core/README.md)
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@fedimint/core-web",
3
+ "description": "Library for building web apps with a fedimint client",
4
+ "version": "0.0.0-canary-20240910231647",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/fedimint/fedimint-web-sdk.git",
8
+ "directory": "packages/core-web"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "wasm",
13
+ "src"
14
+ ],
15
+ "sideEffects": false,
16
+ "type": "module",
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "devDependencies": {
20
+ "@types/node": "^20.14.8",
21
+ "typescript": "^5.2.2"
22
+ },
23
+ "scripts": {
24
+ "build": "pnpm run clean && tsc --project tsconfig.json",
25
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
26
+ "clean:deep": "pnpm run clean && rm -rf node_modules",
27
+ "typecheck": "tsc --noEmit"
28
+ }
29
+ }
@@ -0,0 +1,163 @@
1
+ import init, { RpcHandle, WasmClient } from '../wasm/fedimint_client_wasm.js'
2
+
3
+ type StreamError = {
4
+ error: string
5
+ data: never
6
+ }
7
+
8
+ type StreamSuccess = {
9
+ data: {} | [] | null | undefined | number | string | boolean
10
+ error: never
11
+ }
12
+
13
+ type StreamResult = StreamSuccess | StreamError
14
+
15
+ type Body = string | null | Record<string, string | null>
16
+
17
+ const DEFAULT_CLIENT_NAME = 'fm-default' as const
18
+
19
+ export class FedimintWallet {
20
+ private _fed: WasmClient | null = null
21
+ private initPromise: Promise<void> | null = null
22
+ private openPromise: Promise<void> | null = null
23
+ private resolveOpen: () => void = () => {}
24
+
25
+ constructor(lazy: boolean = false) {
26
+ if (lazy) return
27
+ this.initialize()
28
+ this.openPromise = new Promise((resolve) => {
29
+ this.resolveOpen = resolve
30
+ })
31
+ }
32
+
33
+ // Setup
34
+ async initialize() {
35
+ if (this.initPromise) return this.initPromise
36
+ // this.worker = new Worker(new URL('./wasm.worker.ts', import.meta.url))
37
+ this.initPromise = init().then(() => {
38
+ console.trace('Fedimint Client Wasm Initialization complete')
39
+ })
40
+ return this.initPromise
41
+ }
42
+
43
+ async open(clientName: string = DEFAULT_CLIENT_NAME) {
44
+ await this.initialize()
45
+ const wasm = await WasmClient.open(clientName)
46
+
47
+ if (wasm === undefined) return false
48
+ this._fed = wasm
49
+ this.resolveOpen()
50
+ return true
51
+ }
52
+
53
+ async joinFederation(
54
+ inviteCode: string,
55
+ clientName: string = DEFAULT_CLIENT_NAME,
56
+ ) {
57
+ await this.initialize()
58
+ this._fed = await WasmClient.join_federation(clientName, inviteCode)
59
+ this.resolveOpen()
60
+ }
61
+
62
+ // RPC
63
+ private async _rpcStream(
64
+ module: string,
65
+ method: string,
66
+ body: Body = {},
67
+ onSuccess: (res: StreamSuccess['data']) => void,
68
+ onError: (res: StreamError['error']) => void,
69
+ ): Promise<RpcHandle> {
70
+ await this.openPromise
71
+ if (!this._fed) throw new Error('FedimintWallet is not open')
72
+ const unsubscribe = await this._fed.rpc(
73
+ module,
74
+ method,
75
+ JSON.stringify(body),
76
+ (res: string) => {
77
+ const parsed = JSON.parse(res) as StreamResult
78
+ if (parsed.error) {
79
+ onError(parsed.error)
80
+ } else {
81
+ onSuccess(parsed.data)
82
+ }
83
+ },
84
+ )
85
+ return unsubscribe
86
+ }
87
+
88
+ private async _rpcSingle(module: string, method: string, body: Body = {}) {
89
+ return new Promise((resolve, reject) => {
90
+ if (!this._fed) return reject('FedimintWallet is not open')
91
+ this._fed.rpc(module, method, JSON.stringify(body), (res: string) => {
92
+ const parsed = JSON.parse(res) as StreamResult
93
+ if (parsed.error) {
94
+ reject(parsed.error)
95
+ } else {
96
+ resolve(parsed.data)
97
+ }
98
+ })
99
+ })
100
+ }
101
+
102
+ // Client
103
+
104
+ async getBalance(): Promise<number> {
105
+ return (await this._rpcSingle('', 'get_balance')) as number
106
+ }
107
+
108
+ // LN
109
+
110
+ async payInvoice(invoice: string): Promise<void> {
111
+ await this._rpcSingle('ln', 'pay_bolt11_invoice', {
112
+ invoice,
113
+ })
114
+ }
115
+
116
+ // async listGateways() {
117
+ // return await this._rpcSingle("ln", "list_gateways");
118
+ // }
119
+ //
120
+ // async verifyGateways() {
121
+ // return await this._rpcSingle("ln", "verify_gateway_availability");
122
+ // }
123
+
124
+ // Mint
125
+
126
+ async redeemEcash(notes: string): Promise<void> {
127
+ await this._rpcSingle('mint', 'reissue_external_notes', {
128
+ oob_notes: notes, // "out of band notes"
129
+ extra_meta: null,
130
+ })
131
+ }
132
+
133
+ // Teardown
134
+ async cleanup() {
135
+ await this._fed?.free()
136
+ }
137
+
138
+ isOpen() {
139
+ return this._fed !== null
140
+ }
141
+
142
+ // Streaming
143
+
144
+ subscribeBalance(
145
+ onSuccess: (balance: number) => void = () => {},
146
+ onError: (error: string) => void = () => {},
147
+ ) {
148
+ const unsubscribe = this._rpcStream(
149
+ '',
150
+ 'subscribe_balance_changes',
151
+ {},
152
+ (res) => onSuccess(parseInt(res as string)),
153
+ onError,
154
+ )
155
+
156
+ return () => {
157
+ unsubscribe.then((unsub) => {
158
+ unsub.cancel()
159
+ unsub.free()
160
+ })
161
+ }
162
+ }
163
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { FedimintWallet } from './FedimintWallet.js'
2
+
3
+ export { FedimintWallet }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,41 @@
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
+ // })
@@ -0,0 +1,104 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /**
4
+ */
5
+ export class RpcHandle {
6
+ free(): void;
7
+ /**
8
+ */
9
+ cancel(): void;
10
+ }
11
+ /**
12
+ */
13
+ export class WasmClient {
14
+ free(): void;
15
+ /**
16
+ * Open fedimint client with already joined federation.
17
+ *
18
+ * After you have joined a federation, you can reopen the fedimint client
19
+ * with same client_name. Opening client with same name at same time is
20
+ * not supported. You can close the current client by calling
21
+ * `client.free()`. NOTE: The client will remain active until all the
22
+ * running rpc calls have finished.
23
+ * @param {string} client_name
24
+ * @returns {Promise<WasmClient | undefined>}
25
+ */
26
+ static open(client_name: string): Promise<WasmClient | undefined>;
27
+ /**
28
+ * Open a fedimint client by join a federation.
29
+ * @param {string} client_name
30
+ * @param {string} invite_code
31
+ * @returns {Promise<WasmClient>}
32
+ */
33
+ static join_federation(client_name: string, invite_code: string): Promise<WasmClient>;
34
+ /**
35
+ * Call a fedimint client rpc the responses are returned using `cb`
36
+ * callback. Each rpc call *can* return multiple responses by calling
37
+ * `cb` multiple times. The returned RpcHandle can be used to cancel the
38
+ * operation.
39
+ * @param {string} module
40
+ * @param {string} method
41
+ * @param {string} payload
42
+ * @param {Function} cb
43
+ * @returns {RpcHandle}
44
+ */
45
+ rpc(module: string, method: string, payload: string, cb: Function): RpcHandle;
46
+ }
47
+
48
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
49
+
50
+ export interface InitOutput {
51
+ readonly memory: WebAssembly.Memory;
52
+ readonly __wbg_wasmclient_free: (a: number) => void;
53
+ readonly __wbg_rpchandle_free: (a: number) => void;
54
+ readonly rpchandle_cancel: (a: number) => void;
55
+ readonly wasmclient_open: (a: number, b: number) => number;
56
+ readonly wasmclient_join_federation: (a: number, b: number, c: number, d: number) => number;
57
+ readonly wasmclient_rpc: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => number;
58
+ readonly ring_core_0_17_8_bn_mul_mont: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
59
+ readonly rustsecp256k1zkp_v0_8_0_default_illegal_callback_fn: (a: number, b: number) => void;
60
+ readonly rustsecp256k1zkp_v0_8_0_default_error_callback_fn: (a: number, b: number) => void;
61
+ readonly rustsecp256k1_v0_6_1_context_create: (a: number) => number;
62
+ readonly rustsecp256k1_v0_6_1_context_destroy: (a: number) => void;
63
+ readonly rustsecp256k1_v0_6_1_default_illegal_callback_fn: (a: number, b: number) => void;
64
+ readonly rustsecp256k1_v0_6_1_default_error_callback_fn: (a: number, b: number) => void;
65
+ readonly rustsecp256k1_v0_8_1_context_create: (a: number) => number;
66
+ readonly rustsecp256k1_v0_8_1_context_destroy: (a: number) => void;
67
+ readonly rustsecp256k1_v0_8_1_default_illegal_callback_fn: (a: number, b: number) => void;
68
+ readonly rustsecp256k1_v0_8_1_default_error_callback_fn: (a: number, b: number) => void;
69
+ readonly __wbindgen_malloc: (a: number, b: number) => number;
70
+ readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
71
+ readonly __wbindgen_export_2: WebAssembly.Table;
72
+ readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
73
+ readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h8697e16cd1c64495: (a: number, b: number, c: number, d: number) => void;
74
+ readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h31a94cc9a05d5ca0: (a: number, b: number, c: number) => void;
75
+ readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h4d11ec113460b95d: (a: number, b: number) => void;
76
+ readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__he7056307c6986185: (a: number, b: number, c: number, d: number) => void;
77
+ readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5b16159cdfa166b0: (a: number, b: number) => void;
78
+ readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hebf1bd391d12b3cb: (a: number, b: number, c: number) => void;
79
+ readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h09ee3e3abd173580: (a: number, b: number) => void;
80
+ readonly __wbindgen_free: (a: number, b: number, c: number) => void;
81
+ readonly __wbindgen_exn_store: (a: number) => void;
82
+ readonly wasm_bindgen__convert__closures__invoke2_mut__h8bdaa9faeb7d5075: (a: number, b: number, c: number, d: number) => void;
83
+ }
84
+
85
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
86
+ /**
87
+ * Instantiates the given `module`, which can either be bytes or
88
+ * a precompiled `WebAssembly.Module`.
89
+ *
90
+ * @param {SyncInitInput} module
91
+ *
92
+ * @returns {InitOutput}
93
+ */
94
+ export function initSync(module: SyncInitInput): InitOutput;
95
+
96
+ /**
97
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
98
+ * for everything else, calls `WebAssembly.instantiate` directly.
99
+ *
100
+ * @param {InitInput | Promise<InitInput>} module_or_path
101
+ *
102
+ * @returns {Promise<InitOutput>}
103
+ */
104
+ export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;