@fedimint/core-web 0.0.11 → 0.1.0
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/dist/dts/FedimintWallet.d.ts +4 -62
- package/dist/dts/FedimintWallet.d.ts.map +1 -1
- package/dist/dts/WalletDirector.d.ts +78 -0
- package/dist/dts/WalletDirector.d.ts.map +1 -0
- package/dist/dts/index.d.ts +2 -1
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/services/BalanceService.d.ts +2 -2
- package/dist/dts/services/BalanceService.d.ts.map +1 -1
- package/dist/dts/services/FederationService.d.ts +7 -5
- package/dist/dts/services/FederationService.d.ts.map +1 -1
- package/dist/dts/services/LightningService.d.ts +4 -3
- package/dist/dts/services/LightningService.d.ts.map +1 -1
- package/dist/dts/services/MintService.d.ts +4 -3
- package/dist/dts/services/MintService.d.ts.map +1 -1
- package/dist/dts/services/RecoveryService.d.ts +2 -2
- package/dist/dts/services/RecoveryService.d.ts.map +1 -1
- package/dist/dts/services/WalletService.d.ts +13 -0
- package/dist/dts/services/WalletService.d.ts.map +1 -0
- package/dist/dts/services/index.d.ts +1 -0
- package/dist/dts/services/index.d.ts.map +1 -1
- package/dist/dts/{worker/WorkerClient.d.ts → transport/TransportClient.d.ts} +20 -9
- package/dist/dts/transport/TransportClient.d.ts.map +1 -0
- package/dist/dts/transport/index.d.ts +3 -0
- package/dist/dts/transport/index.d.ts.map +1 -0
- package/dist/dts/transport/wasmTransport/WasmWorkerTransport.d.ts +12 -0
- package/dist/dts/transport/wasmTransport/WasmWorkerTransport.d.ts.map +1 -0
- package/dist/dts/types/index.d.ts +1 -1
- package/dist/dts/types/index.d.ts.map +1 -1
- package/dist/dts/types/transport.d.ts +35 -0
- package/dist/dts/types/transport.d.ts.map +1 -0
- package/dist/dts/types/wallet.d.ts +159 -3
- package/dist/dts/types/wallet.d.ts.map +1 -1
- package/dist/dts/utils/logger.d.ts +10 -3
- package/dist/dts/utils/logger.d.ts.map +1 -1
- package/dist/index.d.ts +275 -29
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/src/FedimintWallet.test.ts +13 -0
- package/src/FedimintWallet.ts +6 -91
- package/src/WalletDirector.ts +118 -0
- package/src/index.ts +2 -1
- package/src/services/BalanceService.test.ts +4 -28
- package/src/services/BalanceService.ts +2 -2
- package/src/services/FederationService.ts +197 -5
- package/src/services/LightningService.test.ts +57 -0
- package/src/services/LightningService.ts +17 -2
- package/src/services/MintService.test.ts +41 -0
- package/src/services/MintService.ts +11 -2
- package/src/services/RecoveryService.ts +2 -2
- package/src/services/WalletService.test.ts +59 -0
- package/src/services/WalletService.ts +50 -0
- package/src/services/index.ts +1 -0
- package/src/test/TestFedimintWallet.ts +13 -8
- package/src/test/TestWalletDirector.ts +14 -0
- package/src/test/TestingService.ts +8 -7
- package/src/test/fixtures.ts +35 -11
- package/src/transport/TransportClient.test.ts +6 -0
- package/src/{worker/WorkerClient.ts → transport/TransportClient.ts} +60 -45
- package/src/transport/index.ts +2 -0
- package/src/transport/wasmTransport/WasmWorkerTransport.ts +39 -0
- package/src/{worker → transport/wasmTransport}/worker.js +28 -0
- package/src/{worker → transport/wasmTransport}/worker.test.ts +3 -3
- package/src/types/index.ts +1 -1
- package/src/types/transport.ts +54 -0
- package/src/types/wallet.ts +186 -2
- package/src/utils/logger.ts +13 -5
- package/dist/dts/types/worker.d.ts +0 -4
- package/dist/dts/types/worker.d.ts.map +0 -1
- package/dist/dts/worker/WorkerClient.d.ts.map +0 -1
- package/dist/dts/worker/index.d.ts +0 -2
- package/dist/dts/worker/index.d.ts.map +0 -1
- package/src/types/worker.ts +0 -15
- package/src/worker/WorkerClient.test.ts +0 -6
- package/src/worker/index.ts +0 -1
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
EcashTransaction,
|
|
3
|
+
LightningTransaction,
|
|
4
|
+
LnVariant,
|
|
5
|
+
MintVariant,
|
|
6
|
+
OperationKey,
|
|
7
|
+
OperationLog,
|
|
8
|
+
Transactions,
|
|
9
|
+
WalletTransaction,
|
|
10
|
+
WalletVariant,
|
|
11
|
+
} from '../types'
|
|
12
|
+
import { TransportClient } from '../transport'
|
|
3
13
|
|
|
4
14
|
export class FederationService {
|
|
5
|
-
constructor(private client:
|
|
15
|
+
constructor(private client: TransportClient) {}
|
|
6
16
|
|
|
7
17
|
async getConfig() {
|
|
8
18
|
return await this.client.rpcSingle('', 'get_config', {})
|
|
@@ -18,7 +28,189 @@ export class FederationService {
|
|
|
18
28
|
})
|
|
19
29
|
}
|
|
20
30
|
|
|
21
|
-
async listOperations(
|
|
22
|
-
|
|
31
|
+
async listOperations(
|
|
32
|
+
limit?: number,
|
|
33
|
+
last_seen?: OperationKey,
|
|
34
|
+
): Promise<[OperationKey, OperationLog][]> {
|
|
35
|
+
return await this.client.rpcSingle<[OperationKey, OperationLog][]>(
|
|
36
|
+
'',
|
|
37
|
+
'list_operations',
|
|
38
|
+
{
|
|
39
|
+
limit: limit ?? null,
|
|
40
|
+
last_seen: last_seen ?? null,
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async getOperation(operationId: string) {
|
|
46
|
+
return await this.client.rpcSingle<OperationLog | null>(
|
|
47
|
+
'',
|
|
48
|
+
'get_operation',
|
|
49
|
+
{ operation_id: operationId },
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async listTransactions(
|
|
54
|
+
limit?: number,
|
|
55
|
+
last_seen?: OperationKey,
|
|
56
|
+
): Promise<Transactions[]> {
|
|
57
|
+
const operations = await this.listOperations(limit, last_seen)
|
|
58
|
+
return operations
|
|
59
|
+
.filter(
|
|
60
|
+
(item): item is [OperationKey, OperationLog] =>
|
|
61
|
+
Array.isArray(item) && item.length === 2,
|
|
62
|
+
)
|
|
63
|
+
.filter(([_, op]) => {
|
|
64
|
+
const { operation_module_kind, meta } = op
|
|
65
|
+
const variant = meta.variant
|
|
66
|
+
return (
|
|
67
|
+
(operation_module_kind === 'ln' &&
|
|
68
|
+
((variant as LnVariant).pay || (variant as LnVariant).receive)) ||
|
|
69
|
+
(operation_module_kind === 'mint' &&
|
|
70
|
+
((variant as MintVariant).spend_o_o_b ||
|
|
71
|
+
(variant as MintVariant).reissuance)) ||
|
|
72
|
+
(operation_module_kind === 'wallet' &&
|
|
73
|
+
((variant as WalletVariant).deposit ||
|
|
74
|
+
(variant as WalletVariant).withdraw))
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
.map(([key, op]) => {
|
|
78
|
+
const timestamp = key.creation_time
|
|
79
|
+
? Math.round(
|
|
80
|
+
key.creation_time.secs_since_epoch * 1000 +
|
|
81
|
+
key.creation_time.nanos_since_epoch / 1_000_000,
|
|
82
|
+
)
|
|
83
|
+
: 0
|
|
84
|
+
const operationId = key.operation_id
|
|
85
|
+
const kind = op.operation_module_kind as 'ln' | 'mint' | 'wallet'
|
|
86
|
+
const meta = op.meta
|
|
87
|
+
const variant = meta.variant
|
|
88
|
+
|
|
89
|
+
let outcome: string | undefined
|
|
90
|
+
if (op.outcome && op.outcome.outcome) {
|
|
91
|
+
if (typeof op.outcome.outcome === 'string') {
|
|
92
|
+
outcome = op.outcome.outcome
|
|
93
|
+
} else if (
|
|
94
|
+
typeof op.outcome.outcome === 'object' &&
|
|
95
|
+
op.outcome.outcome !== null
|
|
96
|
+
) {
|
|
97
|
+
if ('success' in op.outcome.outcome) outcome = 'success'
|
|
98
|
+
else if ('canceled' in op.outcome.outcome) outcome = 'canceled'
|
|
99
|
+
else if ('claimed' in op.outcome.outcome) outcome = 'claimed'
|
|
100
|
+
else if ('funded' in op.outcome.outcome) outcome = 'funded'
|
|
101
|
+
else if ('awaiting_funds' in op.outcome.outcome)
|
|
102
|
+
outcome = 'awaiting_funds'
|
|
103
|
+
else if ('unexpected_error' in op.outcome.outcome)
|
|
104
|
+
outcome = 'unexpected_error'
|
|
105
|
+
else if ('created' in op.outcome.outcome) outcome = 'created'
|
|
106
|
+
else if ('waiting_for_refund' in op.outcome.outcome)
|
|
107
|
+
outcome = 'canceled'
|
|
108
|
+
else if ('awaiting_change' in op.outcome.outcome)
|
|
109
|
+
outcome = 'pending'
|
|
110
|
+
else if ('refunded' in op.outcome.outcome) outcome = 'refunded'
|
|
111
|
+
else if ('waiting_for_payment' in op.outcome.outcome)
|
|
112
|
+
outcome = 'awaiting_funds'
|
|
113
|
+
else if ('Created' in op.outcome.outcome) outcome = 'Created'
|
|
114
|
+
else if ('Success' in op.outcome.outcome) outcome = 'Success'
|
|
115
|
+
else if ('Refunded' in op.outcome.outcome) outcome = 'Refunded'
|
|
116
|
+
else if ('UserCanceledProcessing' in op.outcome.outcome)
|
|
117
|
+
outcome = 'UserCanceledProcessing'
|
|
118
|
+
else if ('UserCanceledSuccess' in op.outcome.outcome)
|
|
119
|
+
outcome = 'UserCanceledSuccess'
|
|
120
|
+
else if ('UserCanceledFailure' in op.outcome.outcome)
|
|
121
|
+
outcome = 'UserCanceledFailure'
|
|
122
|
+
else if ('WaitingForTransaction' in op.outcome.outcome)
|
|
123
|
+
outcome = 'pending'
|
|
124
|
+
else if ('WaitingForConfirmation' in op.outcome.outcome)
|
|
125
|
+
outcome = 'pending'
|
|
126
|
+
else if ('Confirmed' in op.outcome.outcome) outcome = 'Confirmed'
|
|
127
|
+
else if ('Claimed' in op.outcome.outcome) outcome = 'Claimed'
|
|
128
|
+
else if ('Failed' in op.outcome.outcome) outcome = 'Failed'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (kind === 'ln') {
|
|
133
|
+
const isPay = !!(variant as LnVariant).pay
|
|
134
|
+
const txId =
|
|
135
|
+
(variant as LnVariant).pay?.out_point.txid ||
|
|
136
|
+
(variant as LnVariant).receive?.out_point.txid ||
|
|
137
|
+
''
|
|
138
|
+
const type = (variant as LnVariant).pay ? 'send' : 'receive'
|
|
139
|
+
const invoice =
|
|
140
|
+
(variant as LnVariant).pay?.invoice ||
|
|
141
|
+
(variant as LnVariant).receive?.invoice ||
|
|
142
|
+
''
|
|
143
|
+
const gateway =
|
|
144
|
+
(variant as LnVariant).pay?.gateway_id ||
|
|
145
|
+
(variant as LnVariant).receive?.gateway_id ||
|
|
146
|
+
''
|
|
147
|
+
const fee = (variant as LnVariant).pay?.fee
|
|
148
|
+
const internalPay = (variant as LnVariant).pay?.is_internal_payment
|
|
149
|
+
const preimage =
|
|
150
|
+
isPay &&
|
|
151
|
+
op.outcome?.outcome &&
|
|
152
|
+
typeof op.outcome.outcome === 'object' &&
|
|
153
|
+
'success' in op.outcome.outcome
|
|
154
|
+
? op.outcome.outcome.success.preimage
|
|
155
|
+
: undefined
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
timestamp,
|
|
159
|
+
operationId,
|
|
160
|
+
kind,
|
|
161
|
+
txId,
|
|
162
|
+
type,
|
|
163
|
+
invoice,
|
|
164
|
+
internalPay,
|
|
165
|
+
fee,
|
|
166
|
+
gateway,
|
|
167
|
+
outcome: outcome as LightningTransaction['outcome'],
|
|
168
|
+
} as LightningTransaction
|
|
169
|
+
} else if (kind === 'mint') {
|
|
170
|
+
const txId = (variant as MintVariant).reissuance?.txid
|
|
171
|
+
const type = (variant as MintVariant).reissuance
|
|
172
|
+
? 'reissue'
|
|
173
|
+
: 'spend_oob'
|
|
174
|
+
const amountMsats = meta.amount
|
|
175
|
+
const notes = (variant as MintVariant).spend_o_o_b?.oob_notes
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
timestamp,
|
|
179
|
+
type,
|
|
180
|
+
txId,
|
|
181
|
+
outcome: outcome as EcashTransaction['outcome'],
|
|
182
|
+
operationId,
|
|
183
|
+
amountMsats,
|
|
184
|
+
notes,
|
|
185
|
+
kind,
|
|
186
|
+
} as EcashTransaction
|
|
187
|
+
} else if (kind === 'wallet') {
|
|
188
|
+
const type = (variant as WalletVariant).deposit
|
|
189
|
+
? 'deposit'
|
|
190
|
+
: 'withdraw'
|
|
191
|
+
const address =
|
|
192
|
+
(variant as WalletVariant).deposit?.address ||
|
|
193
|
+
(variant as WalletVariant).withdraw?.address ||
|
|
194
|
+
''
|
|
195
|
+
const feeRate = (variant as WalletVariant).withdraw?.fee.fee_rate
|
|
196
|
+
.sats_per_kvb
|
|
197
|
+
const amountMsats =
|
|
198
|
+
(variant as WalletVariant).withdraw?.amountMsats || 0
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
timestamp,
|
|
202
|
+
type,
|
|
203
|
+
onchainAddress: address,
|
|
204
|
+
fee: feeRate || 0,
|
|
205
|
+
amountMsats,
|
|
206
|
+
outcome,
|
|
207
|
+
kind,
|
|
208
|
+
operationId,
|
|
209
|
+
} as WalletTransaction
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
.filter(
|
|
213
|
+
(transaction): transaction is Transactions => transaction !== undefined,
|
|
214
|
+
)
|
|
23
215
|
}
|
|
24
216
|
}
|
|
@@ -206,3 +206,60 @@ walletTest(
|
|
|
206
206
|
expect(subscription).toBeDefined()
|
|
207
207
|
},
|
|
208
208
|
)
|
|
209
|
+
|
|
210
|
+
walletTest(
|
|
211
|
+
'subscribe_internal_pay should return state',
|
|
212
|
+
async ({ fundedWallet }) => {
|
|
213
|
+
expect(fundedWallet).toBeDefined()
|
|
214
|
+
expect(fundedWallet.isOpen()).toBe(true)
|
|
215
|
+
const initialBalance = await fundedWallet.balance.getBalance()
|
|
216
|
+
expect(initialBalance).toBeGreaterThan(0)
|
|
217
|
+
const externalInvoice = (
|
|
218
|
+
await fundedWallet.lightning.createInvoice(1, 'test invoice')
|
|
219
|
+
).invoice
|
|
220
|
+
const payment = await fundedWallet.lightning.payInvoice(externalInvoice)
|
|
221
|
+
expect(payment).toMatchObject({
|
|
222
|
+
contract_id: expect.any(String),
|
|
223
|
+
fee: expect.any(Number),
|
|
224
|
+
payment_type: expect.any(Object),
|
|
225
|
+
})
|
|
226
|
+
const finalBalance = await fundedWallet.balance.getBalance()
|
|
227
|
+
expect(finalBalance).toBeLessThan(initialBalance)
|
|
228
|
+
expect(payment.payment_type).toHaveProperty('internal')
|
|
229
|
+
if ('internal' in payment.payment_type) {
|
|
230
|
+
const id = payment.payment_type.internal
|
|
231
|
+
await new Promise<void>((resolve, reject) => {
|
|
232
|
+
const unsubscribe = fundedWallet.lightning.subscribeInternalPayment(
|
|
233
|
+
id,
|
|
234
|
+
(state) => {
|
|
235
|
+
try {
|
|
236
|
+
expect(state).toBeDefined()
|
|
237
|
+
expect(state).toBe('funding')
|
|
238
|
+
unsubscribe()
|
|
239
|
+
resolve()
|
|
240
|
+
} catch (err) {
|
|
241
|
+
reject(err)
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
})
|
|
246
|
+
} else {
|
|
247
|
+
const id = payment.payment_type.lightning
|
|
248
|
+
await new Promise<void>((resolve, reject) => {
|
|
249
|
+
const unsubscribe = fundedWallet.lightning.subscribeLnPay(
|
|
250
|
+
id,
|
|
251
|
+
(state) => {
|
|
252
|
+
try {
|
|
253
|
+
expect(state).toBeDefined()
|
|
254
|
+
expect(state).toBe('created')
|
|
255
|
+
unsubscribe()
|
|
256
|
+
resolve()
|
|
257
|
+
} catch (err) {
|
|
258
|
+
reject(err)
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
)
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
)
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TransportClient } from '../transport'
|
|
2
2
|
import type {
|
|
3
3
|
CreateBolt11Response,
|
|
4
4
|
GatewayInfo,
|
|
5
5
|
JSONObject,
|
|
6
6
|
LightningGateway,
|
|
7
|
+
LnInternalPayState,
|
|
7
8
|
LnPayState,
|
|
8
9
|
LnReceiveState,
|
|
9
10
|
OutgoingLightningPayment,
|
|
10
11
|
} from '../types'
|
|
11
12
|
|
|
12
13
|
export class LightningService {
|
|
13
|
-
constructor(private client:
|
|
14
|
+
constructor(private client: TransportClient) {}
|
|
14
15
|
|
|
15
16
|
/** https://web.fedimint.org/core/FedimintWallet/LightningService/createInvoice#lightning-createinvoice */
|
|
16
17
|
async createInvoice(
|
|
@@ -141,6 +142,20 @@ export class LightningService {
|
|
|
141
142
|
})
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
subscribeInternalPayment(
|
|
146
|
+
operation_id: string,
|
|
147
|
+
onSuccess: (state: LnInternalPayState) => void = () => {},
|
|
148
|
+
onError: (error: string) => void = () => {},
|
|
149
|
+
) {
|
|
150
|
+
return this.client.rpcStream(
|
|
151
|
+
'ln',
|
|
152
|
+
'subscribe_internal_pay',
|
|
153
|
+
{ operation_id: operation_id },
|
|
154
|
+
onSuccess,
|
|
155
|
+
onError,
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
144
159
|
// TODO: Document
|
|
145
160
|
subscribeLnClaim(
|
|
146
161
|
operationId: string,
|
|
@@ -31,3 +31,44 @@ walletTest('parseNotes should parse notes', async ({ wallet }) => {
|
|
|
31
31
|
|
|
32
32
|
await expect(wallet.mint.reissueExternalNotes('test')).rejects.toThrow()
|
|
33
33
|
})
|
|
34
|
+
|
|
35
|
+
walletTest(
|
|
36
|
+
'getNotesByDenomination should return empty object if wallet is empty',
|
|
37
|
+
async ({ wallet }) => {
|
|
38
|
+
expect(wallet).toBeDefined()
|
|
39
|
+
expect(wallet.isOpen()).toBe(true)
|
|
40
|
+
|
|
41
|
+
const notes = await wallet.mint.getNotesByDenomination()
|
|
42
|
+
const balance = await wallet.balance.getBalance()
|
|
43
|
+
expect(balance).toEqual(0)
|
|
44
|
+
expect(notes).toBeDefined()
|
|
45
|
+
expect(notes).toEqual({})
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
walletTest(
|
|
50
|
+
'getNotesByDenomination should get notes by denomination',
|
|
51
|
+
async ({ fundedWallet }) => {
|
|
52
|
+
expect(fundedWallet).toBeDefined()
|
|
53
|
+
expect(fundedWallet.isOpen()).toBe(true)
|
|
54
|
+
|
|
55
|
+
const notes = await fundedWallet.mint.getNotesByDenomination()
|
|
56
|
+
const balance = await fundedWallet.balance.getBalance()
|
|
57
|
+
expect(balance).toEqual(10000)
|
|
58
|
+
expect(notes).toBeDefined()
|
|
59
|
+
expect(notes).toEqual({
|
|
60
|
+
'1': 2,
|
|
61
|
+
'1024': 3,
|
|
62
|
+
'128': 2,
|
|
63
|
+
'16': 3,
|
|
64
|
+
'2': 3,
|
|
65
|
+
'2048': 2,
|
|
66
|
+
'256': 3,
|
|
67
|
+
'32': 2,
|
|
68
|
+
'4': 2,
|
|
69
|
+
'512': 3,
|
|
70
|
+
'64': 2,
|
|
71
|
+
'8': 2,
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
)
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TransportClient } from '../transport'
|
|
2
2
|
import type {
|
|
3
3
|
Duration,
|
|
4
4
|
JSONObject,
|
|
5
5
|
JSONValue,
|
|
6
6
|
MintSpendNotesResponse,
|
|
7
7
|
MSats,
|
|
8
|
+
NoteCountByDenomination,
|
|
8
9
|
ReissueExternalNotesState,
|
|
9
10
|
SpendNotesState,
|
|
10
11
|
} from '../types'
|
|
11
12
|
|
|
12
13
|
export class MintService {
|
|
13
|
-
constructor(private client:
|
|
14
|
+
constructor(private client: TransportClient) {}
|
|
14
15
|
|
|
15
16
|
/** https://web.fedimint.org/core/FedimintWallet/MintService/redeemEcash */
|
|
16
17
|
async redeemEcash(notes: string) {
|
|
@@ -117,4 +118,12 @@ export class MintService {
|
|
|
117
118
|
operation_id: operationId,
|
|
118
119
|
})
|
|
119
120
|
}
|
|
121
|
+
|
|
122
|
+
async getNotesByDenomination() {
|
|
123
|
+
return await this.client.rpcSingle<NoteCountByDenomination>(
|
|
124
|
+
'mint',
|
|
125
|
+
'note_counts_by_denomination',
|
|
126
|
+
{},
|
|
127
|
+
)
|
|
128
|
+
}
|
|
120
129
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { JSONValue } from '../types'
|
|
2
|
-
import {
|
|
2
|
+
import { TransportClient } from '../transport'
|
|
3
3
|
|
|
4
4
|
export class RecoveryService {
|
|
5
|
-
constructor(private client:
|
|
5
|
+
constructor(private client: TransportClient) {}
|
|
6
6
|
|
|
7
7
|
async hasPendingRecoveries() {
|
|
8
8
|
return await this.client.rpcSingle<boolean>(
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { expect } from 'vitest'
|
|
2
|
+
import { walletTest } from '../test/fixtures'
|
|
3
|
+
import { TxOutputSummary, WalletSummary } from '../types'
|
|
4
|
+
|
|
5
|
+
walletTest(
|
|
6
|
+
'getWalletSummary should return empty object if wallet is empty',
|
|
7
|
+
async ({ wallet }) => {
|
|
8
|
+
expect(wallet).toBeDefined()
|
|
9
|
+
expect(wallet.isOpen()).toBe(true)
|
|
10
|
+
|
|
11
|
+
const balance = await wallet.balance.getBalance()
|
|
12
|
+
expect(balance).toEqual(0)
|
|
13
|
+
|
|
14
|
+
const summary = await wallet.wallet.getWalletSummary()
|
|
15
|
+
const expectedSummary = {
|
|
16
|
+
spendable_utxos: expect.any(Array<TxOutputSummary>),
|
|
17
|
+
unsigned_peg_out_txos: expect.any(Array<TxOutputSummary>),
|
|
18
|
+
unsigned_change_utxos: expect.any(Array<TxOutputSummary>),
|
|
19
|
+
unconfirmed_peg_out_txos: expect.any(Array<TxOutputSummary>),
|
|
20
|
+
unconfirmed_change_utxos: expect.any(Array<TxOutputSummary>),
|
|
21
|
+
} satisfies WalletSummary
|
|
22
|
+
expect(summary).toEqual(expect.objectContaining(expectedSummary))
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
walletTest(
|
|
27
|
+
'generateAddress should always return an address',
|
|
28
|
+
async ({ wallet }) => {
|
|
29
|
+
expect(wallet).toBeDefined()
|
|
30
|
+
expect(wallet.isOpen()).toBe(true)
|
|
31
|
+
const response = await wallet.wallet.generateAddress()
|
|
32
|
+
|
|
33
|
+
expect(response, 'generateAddress').toEqual({
|
|
34
|
+
deposit_address: expect.any(String),
|
|
35
|
+
operation_id: expect.any(String),
|
|
36
|
+
})
|
|
37
|
+
},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
walletTest(
|
|
41
|
+
'sendOnchain should return an operation ID after sending funds',
|
|
42
|
+
async ({ fundedWalletBeefy }) => {
|
|
43
|
+
expect(fundedWalletBeefy).toBeDefined()
|
|
44
|
+
expect(fundedWalletBeefy.isOpen()).toBe(true)
|
|
45
|
+
|
|
46
|
+
const amountSat = 100
|
|
47
|
+
const address =
|
|
48
|
+
'bcrt1qphk8q2v8he2autevdcefnnwjl4yc2hm74uuvhaa6nhrnkd3gfrwq6mnr76'
|
|
49
|
+
|
|
50
|
+
const response = await fundedWalletBeefy.wallet.sendOnchain(
|
|
51
|
+
amountSat,
|
|
52
|
+
address,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
expect(response, 'send onchain').toEqual({
|
|
56
|
+
operation_id: expect.any(String),
|
|
57
|
+
})
|
|
58
|
+
},
|
|
59
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JSONValue,
|
|
3
|
+
WalletSummary,
|
|
4
|
+
GenerateAddressResponse,
|
|
5
|
+
WalletDepositState,
|
|
6
|
+
} from '../types'
|
|
7
|
+
import { TransportClient } from '../transport'
|
|
8
|
+
|
|
9
|
+
export class WalletService {
|
|
10
|
+
constructor(private client: TransportClient) {}
|
|
11
|
+
|
|
12
|
+
async getWalletSummary(): Promise<WalletSummary> {
|
|
13
|
+
return await this.client.rpcSingle('wallet', 'get_wallet_summary', {})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async generateAddress(extraMeta: JSONValue = {}) {
|
|
17
|
+
return await this.client.rpcSingle<GenerateAddressResponse>(
|
|
18
|
+
'wallet',
|
|
19
|
+
'peg_in',
|
|
20
|
+
{
|
|
21
|
+
extra_meta: extraMeta,
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async sendOnchain(
|
|
27
|
+
amountSat: number,
|
|
28
|
+
address: string,
|
|
29
|
+
extraMeta: JSONValue = {},
|
|
30
|
+
): Promise<{ operation_id: string }> {
|
|
31
|
+
return await this.client.rpcSingle('wallet', 'peg_out', {
|
|
32
|
+
amount_sat: amountSat,
|
|
33
|
+
destination_address: address,
|
|
34
|
+
extra_meta: extraMeta,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
subscribeDeposit(
|
|
38
|
+
operation_id: string,
|
|
39
|
+
onSuccess: (state: WalletDepositState) => void = () => {},
|
|
40
|
+
onError: (error: string) => void = () => {},
|
|
41
|
+
) {
|
|
42
|
+
return this.client.rpcStream(
|
|
43
|
+
'ln',
|
|
44
|
+
'subscribe_deposit',
|
|
45
|
+
{ operation_id: operation_id },
|
|
46
|
+
onSuccess,
|
|
47
|
+
onError,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/services/index.ts
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import { FedimintWallet } from '../FedimintWallet'
|
|
2
|
-
import {
|
|
2
|
+
import { TransportClient } from '../transport/TransportClient'
|
|
3
3
|
import { TestingService } from './TestingService'
|
|
4
4
|
|
|
5
5
|
export class TestFedimintWallet extends FedimintWallet {
|
|
6
6
|
public testing: TestingService
|
|
7
7
|
|
|
8
|
-
constructor() {
|
|
9
|
-
super()
|
|
10
|
-
this.testing = new TestingService(
|
|
8
|
+
constructor(_client: TransportClient) {
|
|
9
|
+
super(_client)
|
|
10
|
+
this.testing = new TestingService(_client, this.lightning)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
async fundWallet(
|
|
13
|
+
async fundWallet(amountMSats: number) {
|
|
14
14
|
const info = await this.testing.getFaucetGatewayInfo()
|
|
15
|
-
const invoice = await this.lightning.createInvoice(
|
|
15
|
+
const invoice = await this.lightning.createInvoice(
|
|
16
|
+
amountMSats,
|
|
17
|
+
'',
|
|
18
|
+
1000,
|
|
19
|
+
info,
|
|
20
|
+
)
|
|
16
21
|
await Promise.all([
|
|
17
22
|
this.testing.payFaucetInvoice(invoice.invoice),
|
|
18
23
|
this.lightning.waitForReceive(invoice.operation_id),
|
|
19
24
|
])
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
// Method to expose the
|
|
23
|
-
|
|
27
|
+
// Method to expose the TransportClient
|
|
28
|
+
getTransportClient(): TransportClient {
|
|
24
29
|
return this['_client']
|
|
25
30
|
}
|
|
26
31
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { WasmWorkerTransport } from '../transport/wasmTransport/WasmWorkerTransport'
|
|
2
|
+
import { Transport } from '../types'
|
|
3
|
+
import { WalletDirector } from '../WalletDirector'
|
|
4
|
+
import { TestFedimintWallet } from './TestFedimintWallet'
|
|
5
|
+
|
|
6
|
+
export class TestWalletDirector extends WalletDirector {
|
|
7
|
+
constructor(client: Transport = new WasmWorkerTransport()) {
|
|
8
|
+
super(client)
|
|
9
|
+
}
|
|
10
|
+
async createTestWallet() {
|
|
11
|
+
await this._client.initialize()
|
|
12
|
+
return new TestFedimintWallet(this._client)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { LightningService } from '../services'
|
|
2
|
-
import {
|
|
2
|
+
import { TransportClient } from '../transport'
|
|
3
3
|
|
|
4
4
|
export const TESTING_INVITE =
|
|
5
5
|
'fed11qgqrsdnhwden5te0v9cxjtt4dekxzamxw4kz6mmjvvkhydted9ukg6r9xfsnx7th0fhn26tf093juamwv4u8gtnpwpcz7qqpyz0e327ua8geceutfrcaezwt22mk6s2rdy09kg72jrcmncng2gn0kp2m5sk'
|
|
6
6
|
|
|
7
7
|
// This is a testing service that allows for inspecting the internals
|
|
8
|
-
// of the
|
|
8
|
+
// of the TransportClient. It is not intended for use in production.
|
|
9
9
|
export class TestingService {
|
|
10
10
|
public TESTING_INVITE: string
|
|
11
11
|
constructor(
|
|
12
|
-
private client:
|
|
12
|
+
private client: TransportClient,
|
|
13
13
|
private lightning: LightningService,
|
|
14
14
|
) {
|
|
15
15
|
// Solo Mint on mutinynet
|
|
@@ -25,7 +25,7 @@ export class TestingService {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
async getInviteCode() {
|
|
28
|
-
const res = await fetch(
|
|
28
|
+
const res = await fetch(`${import.meta.env.FAUCET}/connect-string`)
|
|
29
29
|
if (res.ok) {
|
|
30
30
|
return await res.text()
|
|
31
31
|
} else {
|
|
@@ -34,7 +34,7 @@ export class TestingService {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
private async getFaucetGatewayApi() {
|
|
37
|
-
const res = await fetch(
|
|
37
|
+
const res = await fetch(`${import.meta.env.FAUCET}/gateway-api`)
|
|
38
38
|
if (res.ok) {
|
|
39
39
|
return await res.text()
|
|
40
40
|
} else {
|
|
@@ -43,6 +43,7 @@ export class TestingService {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
async getFaucetGatewayInfo() {
|
|
46
|
+
await this.lightning.updateGatewayCache()
|
|
46
47
|
const gateways = await this.lightning.listGateways()
|
|
47
48
|
const api = await this.getFaucetGatewayApi()
|
|
48
49
|
const gateway = gateways.find((g) => g.info.api === api)
|
|
@@ -53,7 +54,7 @@ export class TestingService {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
async payFaucetInvoice(invoice: string) {
|
|
56
|
-
const res = await fetch(
|
|
57
|
+
const res = await fetch(`${import.meta.env.FAUCET}/pay`, {
|
|
57
58
|
method: 'POST',
|
|
58
59
|
body: invoice,
|
|
59
60
|
})
|
|
@@ -65,7 +66,7 @@ export class TestingService {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
async createFaucetInvoice(amount: number) {
|
|
68
|
-
const res = await fetch(
|
|
69
|
+
const res = await fetch(`${import.meta.env.FAUCET}/invoice`, {
|
|
69
70
|
method: 'POST',
|
|
70
71
|
body: amount.toString(),
|
|
71
72
|
})
|