@fedimint/core-web 0.0.3 → 0.0.5
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 +4 -4
- package/dist/FedimintWallet.d.ts +42 -65
- 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 +34 -0
- package/dist/services/BalanceService.d.ts.map +1 -0
- package/dist/services/FederationService.d.ts +11 -0
- package/dist/services/FederationService.d.ts.map +1 -0
- package/dist/services/LightningService.d.ts +18 -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 +16 -11
- package/dist/types/wallet.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/worker/WorkerClient.d.ts +44 -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 +1 -1
- package/dist/worker.js.map +1 -1
- package/package.json +9 -9
- package/src/FedimintWallet.test.ts +77 -0
- package/src/FedimintWallet.ts +91 -466
- package/src/services/BalanceService.test.ts +50 -0
- package/src/services/BalanceService.ts +50 -0
- package/src/services/FederationService.test.ts +58 -0
- package/src/services/FederationService.ts +22 -0
- package/src/services/LightningService.test.ts +168 -0
- package/src/services/LightningService.ts +144 -0
- package/src/services/MintService.test.ts +19 -0
- package/src/services/MintService.ts +96 -0
- package/src/services/RecoveryService.ts +26 -0
- package/src/services/index.ts +5 -0
- package/src/test/TestFedimintWallet.ts +17 -0
- package/src/test/TestingService.ts +59 -0
- package/src/test/setupTests.ts +43 -0
- package/src/types/wallet.ts +20 -13
- package/src/utils/logger.ts +61 -0
- package/src/worker/WorkerClient.ts +229 -0
- package/src/worker/index.ts +1 -0
- package/src/worker/worker.js +96 -0
- package/src/worker/worker.test.ts +90 -0
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm.d.ts +0 -49
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm.js +0 -4
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm_bg.js +0 -1411
- package/node_modules/fedimint-client-wasm/fedimint_client_wasm_bg.wasm +0 -0
- package/node_modules/fedimint-client-wasm/package.json +0 -26
- package/src/worker.js +0 -80
- package/wasm/fedimint_client_wasm.d.ts +0 -49
- package/wasm/fedimint_client_wasm.js +0 -4
- package/wasm/fedimint_client_wasm_bg.js +0 -1411
- package/wasm/fedimint_client_wasm_bg.wasm +0 -0
- package/wasm/fedimint_client_wasm_bg.wasm.d.ts +0 -110
- package/wasm/package.json +0 -26
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
import { walletTest } from '../test/setupTests'
|
|
3
|
+
|
|
4
|
+
walletTest(
|
|
5
|
+
'getConfig should return the federation config',
|
|
6
|
+
async ({ wallet }) => {
|
|
7
|
+
expect(wallet).toBeDefined()
|
|
8
|
+
expect(wallet.isOpen()).toBe(true)
|
|
9
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
10
|
+
await expect(wallet.federation.getConfig()).resolves.toMatchObject({
|
|
11
|
+
api_endpoints: expect.any(Object),
|
|
12
|
+
broadcast_public_keys: expect.any(Object),
|
|
13
|
+
consensus_version: expect.any(Object),
|
|
14
|
+
meta: expect.any(Object),
|
|
15
|
+
modules: expect.any(Object),
|
|
16
|
+
})
|
|
17
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
18
|
+
},
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
walletTest(
|
|
22
|
+
'getFederationId should return the federation id',
|
|
23
|
+
async ({ wallet }) => {
|
|
24
|
+
expect(wallet).toBeDefined()
|
|
25
|
+
expect(wallet.isOpen()).toBe(true)
|
|
26
|
+
|
|
27
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
28
|
+
const federationId = await wallet.federation.getFederationId()
|
|
29
|
+
expect(federationId).toBeTypeOf('string')
|
|
30
|
+
expect(federationId).toHaveLength(64)
|
|
31
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
walletTest(
|
|
36
|
+
'getInviteCode should return the invite code',
|
|
37
|
+
async ({ wallet }) => {
|
|
38
|
+
expect(wallet).toBeDefined()
|
|
39
|
+
expect(wallet.isOpen()).toBe(true)
|
|
40
|
+
|
|
41
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
42
|
+
const inviteCode = await wallet.federation.getInviteCode(0)
|
|
43
|
+
expect(inviteCode).toBeTypeOf('string')
|
|
44
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
walletTest(
|
|
49
|
+
'listOperations should return the list of operations',
|
|
50
|
+
async ({ wallet }) => {
|
|
51
|
+
expect(wallet).toBeDefined()
|
|
52
|
+
expect(wallet.isOpen()).toBe(true)
|
|
53
|
+
|
|
54
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
55
|
+
await expect(wallet.federation.listOperations()).resolves.toMatchObject([])
|
|
56
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
57
|
+
},
|
|
58
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { JSONValue } from '../types/wallet'
|
|
2
|
+
import { WorkerClient } from '../worker'
|
|
3
|
+
|
|
4
|
+
export class FederationService {
|
|
5
|
+
constructor(private client: WorkerClient) {}
|
|
6
|
+
|
|
7
|
+
async getConfig(): Promise<JSONValue> {
|
|
8
|
+
return await this.client.rpcSingle('', 'get_config', {})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getFederationId(): Promise<string> {
|
|
12
|
+
return await this.client.rpcSingle('', 'get_federation_id', {})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async getInviteCode(peer: number): Promise<string | null> {
|
|
16
|
+
return await this.client.rpcSingle('', 'get_invite_code', { peer })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async listOperations(): Promise<JSONValue[]> {
|
|
20
|
+
return await this.client.rpcSingle('', 'list_operations', {})
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
import { walletTest } from '../test/setupTests'
|
|
3
|
+
|
|
4
|
+
walletTest(
|
|
5
|
+
'createInvoice should create a bolt11 invoice',
|
|
6
|
+
async ({ wallet }) => {
|
|
7
|
+
expect(wallet).toBeDefined()
|
|
8
|
+
expect(wallet.isOpen()).toBe(true)
|
|
9
|
+
|
|
10
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
11
|
+
const invoice = await wallet.lightning.createInvoice(100, 'test')
|
|
12
|
+
expect(invoice).toBeDefined()
|
|
13
|
+
expect(invoice).toMatchObject({
|
|
14
|
+
invoice: expect.any(String),
|
|
15
|
+
operation_id: expect.any(String),
|
|
16
|
+
})
|
|
17
|
+
// 3 requests were made, one for the invoice, one for refreshing the
|
|
18
|
+
// gateway cache, one for getting the gateway info
|
|
19
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 3)
|
|
20
|
+
|
|
21
|
+
// Test with expiry time
|
|
22
|
+
await expect(
|
|
23
|
+
wallet.lightning.createInvoice(100, 'test', 1000, {}),
|
|
24
|
+
).resolves.toBeDefined()
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
walletTest(
|
|
29
|
+
'listGateways should return a list of gateways',
|
|
30
|
+
async ({ wallet }) => {
|
|
31
|
+
expect(wallet).toBeDefined()
|
|
32
|
+
expect(wallet.isOpen()).toBe(true)
|
|
33
|
+
|
|
34
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
35
|
+
const gateways = await wallet.lightning.listGateways()
|
|
36
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
37
|
+
expect(gateways).toBeDefined()
|
|
38
|
+
expect(gateways).toMatchObject([
|
|
39
|
+
{
|
|
40
|
+
info: expect.any(Object),
|
|
41
|
+
},
|
|
42
|
+
])
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
walletTest(
|
|
47
|
+
'updateGatewayCache should update the gateway cache',
|
|
48
|
+
async ({ wallet }) => {
|
|
49
|
+
expect(wallet).toBeDefined()
|
|
50
|
+
expect(wallet.isOpen()).toBe(true)
|
|
51
|
+
|
|
52
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
53
|
+
await expect(wallet.lightning.updateGatewayCache()).resolves.toBeDefined()
|
|
54
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
walletTest('getGateway should return a gateway', async ({ wallet }) => {
|
|
59
|
+
expect(wallet).toBeDefined()
|
|
60
|
+
expect(wallet.isOpen()).toBe(true)
|
|
61
|
+
|
|
62
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
63
|
+
const gateway = await wallet.lightning.getGateway()
|
|
64
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
65
|
+
expect(gateway).toMatchObject({
|
|
66
|
+
api: expect.any(String),
|
|
67
|
+
fees: expect.any(Object),
|
|
68
|
+
gateway_id: expect.any(String),
|
|
69
|
+
gateway_redeem_key: expect.any(String),
|
|
70
|
+
lightning_alias: expect.any(String),
|
|
71
|
+
mint_channel_id: expect.any(Number),
|
|
72
|
+
node_pub_key: expect.any(String),
|
|
73
|
+
route_hints: expect.any(Array),
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
walletTest(
|
|
78
|
+
'createInvoiceWithGateway should create a bolt11 invoice with a gateway',
|
|
79
|
+
async ({ wallet }) => {
|
|
80
|
+
expect(wallet).toBeDefined()
|
|
81
|
+
expect(wallet.isOpen()).toBe(true)
|
|
82
|
+
|
|
83
|
+
const gateways = await wallet.lightning.listGateways()
|
|
84
|
+
const gateway = gateways[0]
|
|
85
|
+
expect(gateway).toBeDefined()
|
|
86
|
+
|
|
87
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
88
|
+
const invoice = await wallet.lightning.createInvoiceWithGateway(
|
|
89
|
+
100,
|
|
90
|
+
'test',
|
|
91
|
+
null,
|
|
92
|
+
{},
|
|
93
|
+
gateway.info,
|
|
94
|
+
)
|
|
95
|
+
expect(invoice).toBeDefined()
|
|
96
|
+
expect(invoice).toMatchObject({
|
|
97
|
+
invoice: expect.any(String),
|
|
98
|
+
operation_id: expect.any(String),
|
|
99
|
+
})
|
|
100
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 1)
|
|
101
|
+
await expect(
|
|
102
|
+
wallet.lightning.createInvoiceWithGateway(
|
|
103
|
+
100,
|
|
104
|
+
'test',
|
|
105
|
+
1000,
|
|
106
|
+
{},
|
|
107
|
+
gateway.info,
|
|
108
|
+
),
|
|
109
|
+
).resolves.toBeDefined()
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
walletTest(
|
|
114
|
+
'payInvoice should throw on insufficient funds',
|
|
115
|
+
async ({ wallet }) => {
|
|
116
|
+
expect(wallet).toBeDefined()
|
|
117
|
+
expect(wallet.isOpen()).toBe(true)
|
|
118
|
+
|
|
119
|
+
const invoice = await wallet.lightning.createInvoice(100, 'test')
|
|
120
|
+
expect(invoice).toBeDefined()
|
|
121
|
+
expect(invoice).toMatchObject({
|
|
122
|
+
invoice: expect.any(String),
|
|
123
|
+
operation_id: expect.any(String),
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
const counterBefore = wallet.testing.getRequestCounter()
|
|
127
|
+
// Insufficient funds
|
|
128
|
+
try {
|
|
129
|
+
await wallet.lightning.payInvoice(invoice.invoice, {})
|
|
130
|
+
expect.unreachable('Should throw error')
|
|
131
|
+
} catch (error) {
|
|
132
|
+
expect(error).toBeDefined()
|
|
133
|
+
}
|
|
134
|
+
// 3 requests were made, one for paying the invoice, one for refreshing the
|
|
135
|
+
// gateway cache, one for getting the gateway info
|
|
136
|
+
expect(wallet.testing.getRequestCounter()).toBe(counterBefore + 3)
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
walletTest(
|
|
141
|
+
'payInvoice should pay a bolt11 invoice',
|
|
142
|
+
{ timeout: 45000 },
|
|
143
|
+
async ({ wallet }) => {
|
|
144
|
+
expect(wallet).toBeDefined()
|
|
145
|
+
expect(wallet.isOpen()).toBe(true)
|
|
146
|
+
|
|
147
|
+
const gateways = await wallet.lightning.listGateways()
|
|
148
|
+
const gateway = gateways[0]
|
|
149
|
+
if (!gateway) {
|
|
150
|
+
expect.unreachable('Gateway not found')
|
|
151
|
+
}
|
|
152
|
+
const invoice = await wallet.lightning.createInvoice(10000, 'test')
|
|
153
|
+
await expect(
|
|
154
|
+
wallet.testing.payWithFaucet(invoice.invoice),
|
|
155
|
+
).resolves.toBeDefined()
|
|
156
|
+
await wallet.lightning.waitForReceive(invoice.operation_id)
|
|
157
|
+
// Wait for balance to fully update
|
|
158
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
159
|
+
const externalInvoice = await wallet.testing.getExternalInvoice(10)
|
|
160
|
+
const payment = await wallet.lightning.payInvoice(externalInvoice.pr)
|
|
161
|
+
expect(payment).toBeDefined()
|
|
162
|
+
expect(payment).toMatchObject({
|
|
163
|
+
contract_id: expect.any(String),
|
|
164
|
+
fee: expect.any(Number),
|
|
165
|
+
payment_type: expect.any(Object),
|
|
166
|
+
})
|
|
167
|
+
},
|
|
168
|
+
)
|
|
@@ -0,0 +1,144 @@
|
|
|
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 createInvoiceWithGateway(
|
|
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 createInvoice(
|
|
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 payInvoiceWithGateway(
|
|
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
|
+
private async _getDefaultGatewayInfo(): Promise<LightningGateway> {
|
|
62
|
+
const gateways = await this.listGateways()
|
|
63
|
+
return gateways[0]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async payInvoice(
|
|
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 waitForReceive(operationId: string): Promise<LnReceiveState> {
|
|
112
|
+
return new Promise((resolve, reject) => {
|
|
113
|
+
const unsubscribe = this.subscribeLnReceive(
|
|
114
|
+
operationId,
|
|
115
|
+
(res) => {
|
|
116
|
+
if (res === 'claimed') resolve(res)
|
|
117
|
+
},
|
|
118
|
+
reject,
|
|
119
|
+
)
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
unsubscribe()
|
|
122
|
+
reject(new Error('Timeout waiting for receive'))
|
|
123
|
+
}, 10000)
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async getGateway(
|
|
128
|
+
gatewayId: string | null = null,
|
|
129
|
+
forceInternal: boolean = false,
|
|
130
|
+
): Promise<LightningGateway | null> {
|
|
131
|
+
return await this.client.rpcSingle('ln', 'get_gateway', {
|
|
132
|
+
gateway_id: gatewayId,
|
|
133
|
+
force_internal: forceInternal,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async listGateways(): Promise<LightningGateway[]> {
|
|
138
|
+
return await this.client.rpcSingle('ln', 'list_gateways', {})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async updateGatewayCache(): Promise<JSONValue> {
|
|
142
|
+
return await this.client.rpcSingle('ln', 'update_gateway_cache', {})
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
import { walletTest } from '../test/setupTests'
|
|
3
|
+
|
|
4
|
+
walletTest('redeemEcash should error on invalid ecash', async ({ wallet }) => {
|
|
5
|
+
expect(wallet).toBeDefined()
|
|
6
|
+
expect(wallet.isOpen()).toBe(true)
|
|
7
|
+
|
|
8
|
+
await expect(wallet.mint.redeemEcash('test')).rejects.toThrow()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
walletTest(
|
|
12
|
+
'reissueExternalNotes should reissue external notes',
|
|
13
|
+
async ({ wallet }) => {
|
|
14
|
+
expect(wallet).toBeDefined()
|
|
15
|
+
expect(wallet.isOpen()).toBe(true)
|
|
16
|
+
|
|
17
|
+
await expect(wallet.mint.reissueExternalNotes('test')).rejects.toThrow()
|
|
18
|
+
},
|
|
19
|
+
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { WorkerClient } from '../worker'
|
|
2
|
+
import {
|
|
3
|
+
JSONObject,
|
|
4
|
+
JSONValue,
|
|
5
|
+
ReissueExternalNotesState,
|
|
6
|
+
} from '../types/wallet'
|
|
7
|
+
|
|
8
|
+
export class MintService {
|
|
9
|
+
constructor(private client: WorkerClient) {}
|
|
10
|
+
|
|
11
|
+
async redeemEcash(notes: string): Promise<void> {
|
|
12
|
+
await this.client.rpcSingle('mint', 'reissue_external_notes', {
|
|
13
|
+
oob_notes: notes, // "out of band notes"
|
|
14
|
+
extra_meta: null,
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async reissueExternalNotes(
|
|
19
|
+
oobNotes: string,
|
|
20
|
+
extraMeta: JSONObject = {},
|
|
21
|
+
): Promise<string> {
|
|
22
|
+
return await this.client.rpcSingle('mint', 'reissue_external_notes', {
|
|
23
|
+
oob_notes: oobNotes,
|
|
24
|
+
extra_meta: extraMeta,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
subscribeReissueExternalNotes(
|
|
29
|
+
operationId: string,
|
|
30
|
+
onSuccess: (state: JSONValue) => void = () => {},
|
|
31
|
+
onError: (error: string) => void = () => {},
|
|
32
|
+
) {
|
|
33
|
+
const unsubscribe = this.client.rpcStream<ReissueExternalNotesState>(
|
|
34
|
+
'mint',
|
|
35
|
+
'subscribe_reissue_external_notes',
|
|
36
|
+
{ operation_id: operationId },
|
|
37
|
+
onSuccess,
|
|
38
|
+
onError,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return unsubscribe
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async spendNotes(
|
|
45
|
+
minAmount: number,
|
|
46
|
+
// Tells the wallet to automatically try to cancel the spend if it hasn't completed
|
|
47
|
+
// after the specified number of milliseconds.
|
|
48
|
+
// If the receiver has already redeemed the notes at this time,
|
|
49
|
+
// the notes will not be cancelled
|
|
50
|
+
tryCancelAfter: number = 0,
|
|
51
|
+
includeInvite: boolean = false,
|
|
52
|
+
extraMeta: JSONValue = {},
|
|
53
|
+
): Promise<JSONValue> {
|
|
54
|
+
console.error('tryCancelAfter', tryCancelAfter)
|
|
55
|
+
return await this.client.rpcSingle('mint', 'spend_notes', {
|
|
56
|
+
min_amount: minAmount,
|
|
57
|
+
try_cancel_after: null,
|
|
58
|
+
include_invite: includeInvite,
|
|
59
|
+
extra_meta: extraMeta,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async validateNotes(oobNotes: string): Promise<number> {
|
|
64
|
+
return await this.client.rpcSingle('mint', 'validate_notes', {
|
|
65
|
+
oob_notes: oobNotes,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async tryCancelSpendNotes(operationId: string): Promise<void> {
|
|
70
|
+
await this.client.rpcSingle('mint', 'try_cancel_spend_notes', {
|
|
71
|
+
operation_id: operationId,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
subscribeSpendNotes(
|
|
76
|
+
operationId: string,
|
|
77
|
+
onSuccess: (state: JSONValue) => void = () => {},
|
|
78
|
+
onError: (error: string) => void = () => {},
|
|
79
|
+
) {
|
|
80
|
+
const unsubscribe = this.client.rpcStream(
|
|
81
|
+
'mint',
|
|
82
|
+
'subscribe_spend_notes',
|
|
83
|
+
{ operation_id: operationId },
|
|
84
|
+
(res) => onSuccess(res),
|
|
85
|
+
onError,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return unsubscribe
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async awaitSpendOobRefund(operationId: string): Promise<JSONValue> {
|
|
92
|
+
return await this.client.rpcSingle('mint', 'await_spend_oob_refund', {
|
|
93
|
+
operation_id: operationId,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FedimintWallet } from '../FedimintWallet'
|
|
2
|
+
import { WorkerClient } from '../worker/WorkerClient'
|
|
3
|
+
import { TestingService } from './TestingService'
|
|
4
|
+
|
|
5
|
+
export class TestFedimintWallet extends FedimintWallet {
|
|
6
|
+
public testing: TestingService
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
super()
|
|
10
|
+
this.testing = new TestingService(this.getWorkerClient())
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Method to expose the WorkerClient
|
|
14
|
+
getWorkerClient(): WorkerClient {
|
|
15
|
+
return this['_client']
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { WorkerClient } from '../worker'
|
|
2
|
+
|
|
3
|
+
export const TESTING_INVITE =
|
|
4
|
+
'fed11qgqrsdnhwden5te0v9cxjtt4dekxzamxw4kz6mmjvvkhydted9ukg6r9xfsnx7th0fhn26tf093juamwv4u8gtnpwpcz7qqpyz0e327ua8geceutfrcaezwt22mk6s2rdy09kg72jrcmncng2gn0kp2m5sk'
|
|
5
|
+
|
|
6
|
+
// This is a testing service that allows for inspecting the internals
|
|
7
|
+
// of the WorkerClient. It is not intended for use in production.
|
|
8
|
+
export class TestingService {
|
|
9
|
+
public TESTING_INVITE: string
|
|
10
|
+
constructor(private client: WorkerClient) {
|
|
11
|
+
// Solo Mint on mutinynet
|
|
12
|
+
this.TESTING_INVITE = TESTING_INVITE
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getRequestCounter() {
|
|
16
|
+
return this.client._getRequestCounter()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getRequestCallbackMap() {
|
|
20
|
+
return this.client._getRequestCallbackMap()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async payWithFaucet(invoice: string) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(
|
|
26
|
+
`https://faucet.mutinynet.com/api/lnurlw/callback?k1=k1&pr=${invoice}`,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`HTTP error! Failed to pay invoice. status: ${response.status}`,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return await response.json()
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Error paying with faucet', error)
|
|
38
|
+
throw error
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async getExternalInvoice(amount: number) {
|
|
43
|
+
try {
|
|
44
|
+
const response = await fetch(
|
|
45
|
+
`https://lnurl-staging.mutinywallet.com/lnurlp/refund/callback?amount=${amount}`,
|
|
46
|
+
)
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`HTTP error! Failed to get external invoice. status: ${response.status}`,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return await response.json()
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('Error getting external invoice', error)
|
|
56
|
+
throw error
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expect, test } from 'vitest'
|
|
2
|
+
import { TestFedimintWallet } from './TestFedimintWallet'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Adds Fixtures for setting up and tearing down a test FedimintWallet instance
|
|
6
|
+
*/
|
|
7
|
+
export const walletTest = test.extend<{ wallet: TestFedimintWallet }>({
|
|
8
|
+
wallet: async ({}, use) => {
|
|
9
|
+
const randomTestingId = Math.random().toString(36).substring(2, 15)
|
|
10
|
+
const wallet = new TestFedimintWallet()
|
|
11
|
+
expect(wallet).toBeDefined()
|
|
12
|
+
|
|
13
|
+
await expect(
|
|
14
|
+
wallet.joinFederation(wallet.testing.TESTING_INVITE, randomTestingId),
|
|
15
|
+
).resolves.toBeUndefined()
|
|
16
|
+
await use(wallet)
|
|
17
|
+
|
|
18
|
+
// clear up browser resources
|
|
19
|
+
await wallet.cleanup()
|
|
20
|
+
// remove the wallet db
|
|
21
|
+
indexedDB.deleteDatabase(randomTestingId)
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Adds Fixtures for setting up and tearing down a test Worker instance
|
|
27
|
+
*/
|
|
28
|
+
export const workerTest = test.extend<{
|
|
29
|
+
worker: Worker
|
|
30
|
+
clientName: string
|
|
31
|
+
}>({
|
|
32
|
+
worker: async ({}, use) => {
|
|
33
|
+
const worker = new Worker(new URL('../worker/worker.js', import.meta.url), {
|
|
34
|
+
type: 'module',
|
|
35
|
+
})
|
|
36
|
+
await use(worker)
|
|
37
|
+
worker.terminate()
|
|
38
|
+
},
|
|
39
|
+
clientName: async ({}, use) => {
|
|
40
|
+
const randomTestingId = Math.random().toString(36).substring(2, 15)
|
|
41
|
+
await use(randomTestingId)
|
|
42
|
+
},
|
|
43
|
+
})
|