@mainnet-cash/postgresql-storage 2.1.0-alpha.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 +3 -0
- package/dist/module/SqlProvider.d.ts +30 -0
- package/dist/module/SqlProvider.js +159 -0
- package/dist/module/SqlProvider.js.map +1 -0
- package/dist/module/index.d.ts +2 -0
- package/dist/module/index.js +3 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/util.d.ts +7 -0
- package/dist/module/util.js +24 -0
- package/dist/module/util.js.map +1 -0
- package/dist/module/webhook/Webhook.d.ts +35 -0
- package/dist/module/webhook/Webhook.js +77 -0
- package/dist/module/webhook/Webhook.js.map +1 -0
- package/dist/module/webhook/WebhookBch.d.ts +13 -0
- package/dist/module/webhook/WebhookBch.js +141 -0
- package/dist/module/webhook/WebhookBch.js.map +1 -0
- package/dist/module/webhook/WebhookWorker.d.ts +22 -0
- package/dist/module/webhook/WebhookWorker.js +94 -0
- package/dist/module/webhook/WebhookWorker.js.map +1 -0
- package/dist/module/webhook/index.d.ts +4 -0
- package/dist/module/webhook/index.js +5 -0
- package/dist/module/webhook/index.js.map +1 -0
- package/dist/module/webhook/interface.d.ts +7 -0
- package/dist/module/webhook/interface.js +2 -0
- package/dist/module/webhook/interface.js.map +1 -0
- package/dist/tsconfig.browser.tsbuildinfo +1 -0
- package/package.json +34 -0
- package/src/SqlProvider.test.ts +264 -0
- package/src/SqlProvider.ts +233 -0
- package/src/Wallet.test.ts +571 -0
- package/src/createWallet.test.ts +158 -0
- package/src/index.test.ts +67 -0
- package/src/index.ts +2 -0
- package/src/util.ts +30 -0
- package/src/webhook/Webhook.test.ts +9 -0
- package/src/webhook/Webhook.ts +99 -0
- package/src/webhook/WebhookBch.test.ts +323 -0
- package/src/webhook/WebhookBch.ts +198 -0
- package/src/webhook/WebhookWorker.test.ts +94 -0
- package/src/webhook/WebhookWorker.ts +119 -0
- package/src/webhook/index.ts +4 -0
- package/src/webhook/interface.ts +7 -0
- package/tsconfig.browser.json +6 -0
- package/tsconfig.json +28 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { BaseWallet, WalletTypeEnum } from "mainnet-js";
|
|
2
|
+
import { UnitEnum } from "mainnet-js";
|
|
3
|
+
import { Wallet, RegTestWallet, TestNetWallet } from "mainnet-js";
|
|
4
|
+
import { createWallet } from "mainnet-js";
|
|
5
|
+
import { BalanceResponse } from "mainnet-js";
|
|
6
|
+
import { ExchangeRate } from "mainnet-js";
|
|
7
|
+
import { initProviders, disconnectProviders } from "mainnet-js";
|
|
8
|
+
import { toUtxoId } from "mainnet-js";
|
|
9
|
+
import { Config } from "mainnet-js";
|
|
10
|
+
import { default as SqlProvider } from "./SqlProvider.js";
|
|
11
|
+
|
|
12
|
+
BaseWallet.StorageProvider = SqlProvider;
|
|
13
|
+
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
await initProviders();
|
|
16
|
+
});
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
await disconnectProviders();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe(`Test Wallet library`, () => {
|
|
22
|
+
test("Should get a random regtest wallet", async () => {
|
|
23
|
+
let alice = await RegTestWallet.newRandom("alice_random");
|
|
24
|
+
expect(alice.cashaddr!.slice(0, 8)).toBe("bchreg:q");
|
|
25
|
+
expect(alice.getDepositAddress()!.slice(0, 8)).toBe("bchreg:q");
|
|
26
|
+
const aliceBalance = (await alice.getBalance()) as BalanceResponse;
|
|
27
|
+
expect(aliceBalance.bch).toBe(0);
|
|
28
|
+
expect(aliceBalance.usd).toBe(0);
|
|
29
|
+
expect(await alice.getBalance("sat")).toBe(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("Should get the regtest wallet balance", async () => {
|
|
33
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
34
|
+
if (!process.env.PRIVATE_WIF) {
|
|
35
|
+
throw Error("Attempted to pass an empty WIF");
|
|
36
|
+
} else {
|
|
37
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
38
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
39
|
+
const aliceBalance = (await alice.getBalance()) as BalanceResponse;
|
|
40
|
+
expect(aliceBalance.bch).toBeGreaterThan(5000);
|
|
41
|
+
expect(await alice.getBalance("sat")).toBeGreaterThan(5000 * 100000000);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("Should get a regtest wallet fromId", async () => {
|
|
46
|
+
let alice = await RegTestWallet.fromId(
|
|
47
|
+
`wif:regtest:${process.env.PRIVATE_WIF}`
|
|
48
|
+
);
|
|
49
|
+
expect(alice.cashaddr!.slice(0, 8)).toBe("bchreg:q");
|
|
50
|
+
expect(alice.getDepositAddress()!.slice(0, 8)).toBe("bchreg:q");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("Should get a testnet wallet fromId", async () => {
|
|
54
|
+
let alice = await TestNetWallet.fromId(
|
|
55
|
+
`wif:testnet:${process.env.PRIVATE_WIF}`
|
|
56
|
+
);
|
|
57
|
+
expect(alice.cashaddr!.slice(0, 9)).toBe("bchtest:q");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("Should get a wallet fromId", async () => {
|
|
61
|
+
let alice = await Wallet.newRandom();
|
|
62
|
+
let alice2 = await Wallet.fromId(`wif:mainnet:${alice.privateKeyWif}`);
|
|
63
|
+
expect(alice2.cashaddr).toBe(alice.cashaddr);
|
|
64
|
+
expect(alice.getDepositAddress()!.slice(0, 13)).toBe("bitcoincash:q");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("Should also throw error on wif/network mismatch", async () => {
|
|
68
|
+
expect.assertions(1);
|
|
69
|
+
try {
|
|
70
|
+
await TestNetWallet.fromId(
|
|
71
|
+
`wif:testnet:KysvoRyDkxQycBGj49K8oC3minAfoXnVmkcgx6UsZx3g2VvyGCAa`
|
|
72
|
+
);
|
|
73
|
+
} catch (e: any) {
|
|
74
|
+
expect(e.message).toBe(
|
|
75
|
+
"Testnet type wif KysvoRyDkxQycBGj49K8oC3minAfoXnVmkcgx6UsZx3g2VvyGCAa passed, should start with c"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("Should get an error passing wrong walletType", async () => {
|
|
81
|
+
expect.assertions(1);
|
|
82
|
+
try {
|
|
83
|
+
await RegTestWallet.fromId(`hd:regtest:${process.env.PRIVATE_WIF}`);
|
|
84
|
+
} catch (e: any) {
|
|
85
|
+
expect(e.message.slice(0, 97)).toBe("Unknown wallet type 'hd'");
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("Should get an error passing wrong network to fromId", async () => {
|
|
90
|
+
expect.assertions(1);
|
|
91
|
+
try {
|
|
92
|
+
await TestNetWallet.fromId(`wif:regtest:${process.env.PRIVATE_WIF}`);
|
|
93
|
+
} catch (e: any) {
|
|
94
|
+
expect(e.message.slice(0, 97)).toBe(
|
|
95
|
+
"Network prefix regtest to a testnet wallet"
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// TODO fix this error message
|
|
101
|
+
// test("Should throw a nice error when attempting to send fractional satoshi", async () => {
|
|
102
|
+
// expect.assertions(1);
|
|
103
|
+
// try {
|
|
104
|
+
// let alice = await RegTestWallet.fromId(
|
|
105
|
+
// `wif:regtest:${process.env.PRIVATE_WIF}`
|
|
106
|
+
// );
|
|
107
|
+
// const bob = await createWallet({
|
|
108
|
+
// type: WalletTypeEnum.Wif,
|
|
109
|
+
// network: "regtest",
|
|
110
|
+
// });
|
|
111
|
+
// await alice.send([
|
|
112
|
+
// {
|
|
113
|
+
// cashaddr: bob.cashaddr!,
|
|
114
|
+
// value: 1100.33333,
|
|
115
|
+
// unit: "satoshis",
|
|
116
|
+
// },
|
|
117
|
+
// ]);
|
|
118
|
+
// } catch (e:any) {
|
|
119
|
+
// expect(e.message).toBe(
|
|
120
|
+
// "Cannot send 1100.33333 satoshis, (fractional sats do not exist, yet), please use an integer number."
|
|
121
|
+
// );
|
|
122
|
+
// }
|
|
123
|
+
// });
|
|
124
|
+
|
|
125
|
+
test("Should get the regtest wallet balance", async () => {
|
|
126
|
+
// Build Alice's wallet from Wallet Import Format string, check sats
|
|
127
|
+
if (!process.env.PRIVATE_WIF) {
|
|
128
|
+
throw Error("Attempted to pass an empty WIF");
|
|
129
|
+
} else {
|
|
130
|
+
let alice = await RegTestWallet.fromId(
|
|
131
|
+
`wif:regtest:${process.env.PRIVATE_WIF}`
|
|
132
|
+
); // insert WIF from #1
|
|
133
|
+
expect(await alice.getBalance("sat")).toBeGreaterThan(5000 * 100000000);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("Should send a transaction on the regression network", async () => {
|
|
138
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
139
|
+
if (!process.env.PRIVATE_WIF) {
|
|
140
|
+
throw Error("Attempted to pass an empty WIF");
|
|
141
|
+
} else {
|
|
142
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
143
|
+
const bob = await createWallet({
|
|
144
|
+
type: WalletTypeEnum.Wif,
|
|
145
|
+
network: "regtest",
|
|
146
|
+
});
|
|
147
|
+
let sendResponse = await alice.send([
|
|
148
|
+
{
|
|
149
|
+
cashaddr: bob.cashaddr!,
|
|
150
|
+
value: 1100,
|
|
151
|
+
unit: "satoshis",
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
expect(sendResponse!.txId!.length).toBe(64);
|
|
155
|
+
expect(sendResponse.balance!.bch).toBeGreaterThan(0.01);
|
|
156
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
157
|
+
const bobBalance = (await bob.getBalance()) as BalanceResponse;
|
|
158
|
+
expect(bobBalance.sat).toBe(1100);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("Should send a transaction from specific utxos", async () => {
|
|
163
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
164
|
+
if (!process.env.PRIVATE_WIF) {
|
|
165
|
+
throw Error("Attempted to pass an empty WIF");
|
|
166
|
+
} else {
|
|
167
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
168
|
+
const bob = await createWallet({
|
|
169
|
+
type: WalletTypeEnum.Wif,
|
|
170
|
+
network: "regtest",
|
|
171
|
+
});
|
|
172
|
+
const charlie = await createWallet({
|
|
173
|
+
type: WalletTypeEnum.Wif,
|
|
174
|
+
network: "regtest",
|
|
175
|
+
});
|
|
176
|
+
let sendResponse = await alice.send([
|
|
177
|
+
{
|
|
178
|
+
cashaddr: bob.cashaddr!,
|
|
179
|
+
value: 1001,
|
|
180
|
+
unit: "satoshis",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
cashaddr: bob.cashaddr!,
|
|
184
|
+
value: 1000,
|
|
185
|
+
unit: "satoshis",
|
|
186
|
+
},
|
|
187
|
+
// Using a larger amount here to test that it would indeed return as change to the sender when specified later.
|
|
188
|
+
{
|
|
189
|
+
cashaddr: bob.cashaddr!,
|
|
190
|
+
value: 10001,
|
|
191
|
+
unit: "satoshis",
|
|
192
|
+
},
|
|
193
|
+
// Using a second larger amount to assure that if too many utxos are specified, money the utxo isn't used
|
|
194
|
+
{
|
|
195
|
+
cashaddr: bob.cashaddr!,
|
|
196
|
+
value: 10001,
|
|
197
|
+
unit: "satoshis",
|
|
198
|
+
},
|
|
199
|
+
]);
|
|
200
|
+
let bobBalance = (await bob.getBalance()) as BalanceResponse;
|
|
201
|
+
expect(bobBalance.sat).toBe(22003);
|
|
202
|
+
let bobUtxos = await bob.getUtxos();
|
|
203
|
+
expect(bobUtxos.length).toBe(4);
|
|
204
|
+
|
|
205
|
+
// Filter the list to only odd value utxos
|
|
206
|
+
let oddUtxoIds = bobUtxos
|
|
207
|
+
.filter((utxo) => utxo.satoshis % 2 == 1)
|
|
208
|
+
.map((utxo) => {
|
|
209
|
+
return toUtxoId(utxo);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
213
|
+
let sendResponse2 = await bob.send(
|
|
214
|
+
[
|
|
215
|
+
{
|
|
216
|
+
cashaddr: charlie.cashaddr!,
|
|
217
|
+
value: 1675,
|
|
218
|
+
unit: "satoshis",
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
{ utxoIds: oddUtxoIds }
|
|
222
|
+
);
|
|
223
|
+
expect(sendResponse2.balance!.sat).toBe(19967);
|
|
224
|
+
expect(await charlie.getBalance("sat")).toBe(1675);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("Should send a transaction with change to different change address", async () => {
|
|
229
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
230
|
+
if (!process.env.PRIVATE_WIF) {
|
|
231
|
+
throw Error("Attempted to pass an empty WIF");
|
|
232
|
+
} else {
|
|
233
|
+
let funder = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
234
|
+
|
|
235
|
+
const alice = await createWallet({
|
|
236
|
+
type: WalletTypeEnum.Wif,
|
|
237
|
+
network: "regtest",
|
|
238
|
+
});
|
|
239
|
+
const bob = await createWallet({
|
|
240
|
+
type: WalletTypeEnum.Wif,
|
|
241
|
+
network: "regtest",
|
|
242
|
+
});
|
|
243
|
+
const charlie = await createWallet({
|
|
244
|
+
type: WalletTypeEnum.Wif,
|
|
245
|
+
network: "regtest",
|
|
246
|
+
});
|
|
247
|
+
await funder.send([
|
|
248
|
+
{
|
|
249
|
+
cashaddr: alice.cashaddr!,
|
|
250
|
+
value: 3000,
|
|
251
|
+
unit: "satoshis",
|
|
252
|
+
},
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
let sendResponse = await alice.send(
|
|
256
|
+
[
|
|
257
|
+
{
|
|
258
|
+
cashaddr: bob.cashaddr!,
|
|
259
|
+
value: 1000,
|
|
260
|
+
unit: "satoshis",
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
{
|
|
264
|
+
changeAddress: charlie.cashaddr!,
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
expect(await bob.getBalance("sat")).toBe(1000);
|
|
268
|
+
expect(await charlie.getBalance("sat")).toBe(1780);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("Should send maximum amount from specific utxos", async () => {
|
|
273
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
274
|
+
if (!process.env.PRIVATE_WIF) {
|
|
275
|
+
throw Error("Attempted to pass an empty WIF");
|
|
276
|
+
} else {
|
|
277
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
278
|
+
const bob = await createWallet({
|
|
279
|
+
type: WalletTypeEnum.Wif,
|
|
280
|
+
network: "regtest",
|
|
281
|
+
});
|
|
282
|
+
const charlie = await createWallet({
|
|
283
|
+
type: WalletTypeEnum.Wif,
|
|
284
|
+
network: "regtest",
|
|
285
|
+
});
|
|
286
|
+
let sendResponse = await alice.send([
|
|
287
|
+
{
|
|
288
|
+
cashaddr: bob.cashaddr!,
|
|
289
|
+
value: 1001,
|
|
290
|
+
unit: "satoshis",
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
cashaddr: bob.cashaddr!,
|
|
294
|
+
value: 1000,
|
|
295
|
+
unit: "satoshis",
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
cashaddr: bob.cashaddr!,
|
|
299
|
+
value: 1001,
|
|
300
|
+
unit: "satoshis",
|
|
301
|
+
},
|
|
302
|
+
]);
|
|
303
|
+
let bobBalance = (await bob.getBalance()) as BalanceResponse;
|
|
304
|
+
expect(bobBalance.sat).toBe(3002);
|
|
305
|
+
let bobUtxos = await bob.getUtxos();
|
|
306
|
+
expect(bobUtxos.length).toBe(3);
|
|
307
|
+
|
|
308
|
+
// Filter the list to only odd value utxos
|
|
309
|
+
let oddUtxoIds = bobUtxos
|
|
310
|
+
.filter((utxo) => utxo.satoshis % 2 == 1)
|
|
311
|
+
.map((utxo) => {
|
|
312
|
+
return toUtxoId(utxo);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
316
|
+
let sendResponse2 = await bob.sendMax(charlie.cashaddr!, {
|
|
317
|
+
utxoIds: oddUtxoIds,
|
|
318
|
+
});
|
|
319
|
+
expect(sendResponse2.balance!.sat).toBe(1000);
|
|
320
|
+
expect(await charlie.getBalance("sat")).toBeGreaterThan(1500);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("Send a transaction in dollars regression network", async () => {
|
|
325
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
326
|
+
if (!process.env.PRIVATE_WIF) {
|
|
327
|
+
throw Error("Attempted to pass an empty WIF");
|
|
328
|
+
} else {
|
|
329
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
330
|
+
const bob = await createWallet({
|
|
331
|
+
type: WalletTypeEnum.Wif,
|
|
332
|
+
network: "regtest",
|
|
333
|
+
});
|
|
334
|
+
let usdRate = await ExchangeRate.get("usd");
|
|
335
|
+
await alice.send([[bob.cashaddr!, usdRate, "Usd"]]);
|
|
336
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
337
|
+
const bobBalance = (await bob.getBalance()) as BalanceResponse;
|
|
338
|
+
|
|
339
|
+
expect(Math.round(bobBalance.usd!)).toBe(Math.round(usdRate));
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// If the change from a transaction is less than the DUST_UTXO_THRESHOLD
|
|
344
|
+
// assume that the change cannot be spent and use it as fee instead
|
|
345
|
+
test("Send assume change less than dust is fee", async () => {
|
|
346
|
+
if (!process.env.PRIVATE_WIF) {
|
|
347
|
+
throw Error("Attempted to pass an empty WIF");
|
|
348
|
+
} else {
|
|
349
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
350
|
+
const bob = await createWallet({
|
|
351
|
+
type: WalletTypeEnum.Wif,
|
|
352
|
+
network: "regtest",
|
|
353
|
+
});
|
|
354
|
+
const charlie = await createWallet({
|
|
355
|
+
type: WalletTypeEnum.Wif,
|
|
356
|
+
network: "regtest",
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
await alice.send([[bob.cashaddr!, 1440, "sat"]]);
|
|
360
|
+
await bob.send([[charlie.cashaddr!, 734, "sat"]]);
|
|
361
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
362
|
+
const bobBalance = (await bob.getBalance()) as BalanceResponse;
|
|
363
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
364
|
+
const charlieBalance = (await charlie.getBalance()) as BalanceResponse;
|
|
365
|
+
expect(Math.round(bobBalance.sat!)).toBe(0);
|
|
366
|
+
expect(Math.round(charlieBalance.sat!)).toBe(734);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("Send a transaction (as array) on the regression network", async () => {
|
|
371
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
372
|
+
if (!process.env.PRIVATE_WIF) {
|
|
373
|
+
throw Error("Attempted to pass an empty WIF");
|
|
374
|
+
} else {
|
|
375
|
+
let alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF); // insert WIF from #1
|
|
376
|
+
const bob = await createWallet({
|
|
377
|
+
network: "regtest",
|
|
378
|
+
});
|
|
379
|
+
await alice.send([[bob.cashaddr!, 1200, UnitEnum.SAT]]);
|
|
380
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
381
|
+
const bobBalance = (await bob.getBalance()) as BalanceResponse;
|
|
382
|
+
expect(bobBalance.sat).toBe(1200);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("Should get a random testnet wallet", async () => {
|
|
387
|
+
let alice = await TestNetWallet.newRandom();
|
|
388
|
+
const aliceBalance = (await alice.getBalance()) as BalanceResponse;
|
|
389
|
+
expect(alice.cashaddr!.slice(0, 9)).toBe("bchtest:q");
|
|
390
|
+
expect(aliceBalance.bch).toBe(0);
|
|
391
|
+
expect(aliceBalance.usd).toBe(0);
|
|
392
|
+
expect(await alice.getBalance("sat")).toBe(0);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("Send a transaction on regtest, send it back", async () => {
|
|
396
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
397
|
+
|
|
398
|
+
const alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF!);
|
|
399
|
+
const bob = await createWallet({
|
|
400
|
+
type: WalletTypeEnum.Seed,
|
|
401
|
+
network: "regtest",
|
|
402
|
+
name: "Bob's random wallet",
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
if (!alice.cashaddr || !bob.cashaddr) {
|
|
406
|
+
throw Error("Alice or Bob's wallet are missing addresses");
|
|
407
|
+
}
|
|
408
|
+
if (!alice.privateKey || !bob.privateKey) {
|
|
409
|
+
throw Error("Alice or Bob's wallet are missing private keys");
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Assume fulcrum node polling is 1s
|
|
413
|
+
await alice.send([
|
|
414
|
+
{
|
|
415
|
+
cashaddr: bob.cashaddr,
|
|
416
|
+
value: 3400,
|
|
417
|
+
unit: UnitEnum.SAT,
|
|
418
|
+
},
|
|
419
|
+
]);
|
|
420
|
+
|
|
421
|
+
// Build Bob's wallet from a public address, check his balance.
|
|
422
|
+
const sendMaxResponse = await bob.sendMax(alice.cashaddr);
|
|
423
|
+
expect(sendMaxResponse.txId!.length).toBe(64);
|
|
424
|
+
|
|
425
|
+
//
|
|
426
|
+
const bobBalanceFinal = (await bob.getBalance()) as BalanceResponse;
|
|
427
|
+
expect(bobBalanceFinal.sat).toBe(0);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("Send all coins back on regtest", async () => {
|
|
431
|
+
// Build Alice's wallet from Wallet Import Format string, send some sats
|
|
432
|
+
|
|
433
|
+
const alice = await RegTestWallet.fromWIF(process.env.PRIVATE_WIF!);
|
|
434
|
+
const bob = await createWallet({
|
|
435
|
+
type: WalletTypeEnum.Seed,
|
|
436
|
+
network: "regtest",
|
|
437
|
+
name: "Bob's random wallet",
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const charlie = await createWallet({
|
|
441
|
+
type: WalletTypeEnum.Seed,
|
|
442
|
+
network: "regtest",
|
|
443
|
+
name: "Charlie's random wallet",
|
|
444
|
+
});
|
|
445
|
+
if (!alice.cashaddr || !bob.cashaddr) {
|
|
446
|
+
throw Error("Alice or Bob's wallet are missing addresses");
|
|
447
|
+
}
|
|
448
|
+
if (!alice.privateKey || !bob.privateKey) {
|
|
449
|
+
throw Error("Alice or Bob's wallet are missing private keys");
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Assume fulcrum node polling is 1s
|
|
453
|
+
await alice.send([
|
|
454
|
+
{
|
|
455
|
+
cashaddr: bob.cashaddr,
|
|
456
|
+
value: 3400,
|
|
457
|
+
unit: UnitEnum.SAT,
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
cashaddr: bob.cashaddr,
|
|
461
|
+
value: 3400,
|
|
462
|
+
unit: UnitEnum.SAT,
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
cashaddr: bob.cashaddr,
|
|
466
|
+
value: 3400,
|
|
467
|
+
unit: UnitEnum.SAT,
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
cashaddr: bob.cashaddr,
|
|
471
|
+
value: 3400,
|
|
472
|
+
unit: UnitEnum.SAT,
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
cashaddr: bob.cashaddr,
|
|
476
|
+
value: 3400,
|
|
477
|
+
unit: UnitEnum.SAT,
|
|
478
|
+
},
|
|
479
|
+
]);
|
|
480
|
+
|
|
481
|
+
// Send ALL of Bob's coins to Charlie.
|
|
482
|
+
const sendMaxResponse = await bob.sendMax(charlie.cashaddr!);
|
|
483
|
+
expect(sendMaxResponse.txId!.length).toBe(64);
|
|
484
|
+
expect(sendMaxResponse.balance!.sat!).toBe(0);
|
|
485
|
+
|
|
486
|
+
const bobFinalBalance = await bob.getBalance("sat");
|
|
487
|
+
expect(bobFinalBalance).toBe(0);
|
|
488
|
+
|
|
489
|
+
// Send ALL of Charlie's coins to Alice.
|
|
490
|
+
const sendMaxResponse2 = await charlie.sendMax(alice.cashaddr);
|
|
491
|
+
expect(sendMaxResponse2.txId!.length).toBe(64);
|
|
492
|
+
expect(sendMaxResponse2.balance!.sat!).toBe(0);
|
|
493
|
+
|
|
494
|
+
const charlieFinalBalance = await charlie.getBalance("sat");
|
|
495
|
+
expect(charlieFinalBalance).toBe(0);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test("Set default derivation path", async () => {
|
|
499
|
+
const savedDerivationPath = Config.DefaultParentDerivationPath;
|
|
500
|
+
|
|
501
|
+
const wallet = await Wallet.newRandom();
|
|
502
|
+
expect(wallet.parentDerivationPath).toBe("m/44'/0'/0'");
|
|
503
|
+
expect(wallet.derivationPath).toBe("m/44'/0'/0'/0/0");
|
|
504
|
+
|
|
505
|
+
Config.DefaultParentDerivationPath = "m/44'/145'/0'";
|
|
506
|
+
const otherWallet = await Wallet.newRandom();
|
|
507
|
+
expect(otherWallet.parentDerivationPath).toBe("m/44'/145'/0'");
|
|
508
|
+
expect(otherWallet.derivationPath).toBe("m/44'/145'/0'/0/0");
|
|
509
|
+
|
|
510
|
+
const binsAreEqual = (a, b) => {
|
|
511
|
+
if (a.length !== b.length) {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
// eslint-disable-next-line functional/no-let, functional/no-loop-statement, no-plusplus
|
|
515
|
+
for (let i = 0; i < a.length; i++) {
|
|
516
|
+
if (a[i] !== b[i]) {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return true;
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
expect(binsAreEqual(wallet.privateKey!, otherWallet.privateKey!)).toBe(
|
|
524
|
+
false
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
Config.DefaultParentDerivationPath = savedDerivationPath;
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
describe(`Tests named wallet creation`, () => {
|
|
532
|
+
test("Expect a nameless named wallet to error", async () => {
|
|
533
|
+
expect.assertions(1);
|
|
534
|
+
try {
|
|
535
|
+
await Wallet.named("");
|
|
536
|
+
} catch (e: any) {
|
|
537
|
+
expect(e.message).toBe("Named wallets must have a non-empty name");
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test("Expect force saving over a named wallet to fail", async () => {
|
|
542
|
+
expect.assertions(1);
|
|
543
|
+
try {
|
|
544
|
+
await RegTestWallet.named("duplicate_name", "dup_test");
|
|
545
|
+
await RegTestWallet.named("duplicate_name", "dup_test", true);
|
|
546
|
+
} catch (e: any) {
|
|
547
|
+
expect(e.message).toBe(
|
|
548
|
+
"A wallet with the name duplicate_name already exists in dup_test"
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
test("Store and replace a Regtest wallet", async () => {
|
|
554
|
+
const name = `storereplace ${Math.random()}`;
|
|
555
|
+
expect(await RegTestWallet.namedExists(name)).toBe(false);
|
|
556
|
+
let w1 = await RegTestWallet.named(name);
|
|
557
|
+
expect(await RegTestWallet.namedExists(name)).toBe(true);
|
|
558
|
+
|
|
559
|
+
let seedId = (
|
|
560
|
+
await RegTestWallet.fromSeed(new Array(12).join("abandon "))
|
|
561
|
+
).toDbString();
|
|
562
|
+
let w3 = await RegTestWallet.replaceNamed(name, seedId);
|
|
563
|
+
let w4 = await RegTestWallet.named(name);
|
|
564
|
+
expect(w4.toDbString()).not.toBe(w1.toDbString());
|
|
565
|
+
expect(w4.toDbString()).toBe(seedId);
|
|
566
|
+
|
|
567
|
+
let w5 = await RegTestWallet.replaceNamed(`${name}_nonexistent`, seedId);
|
|
568
|
+
let w6 = await RegTestWallet.named(`${name}_nonexistent`);
|
|
569
|
+
expect(w6.toDbString()).toBe(w5.toDbString());
|
|
570
|
+
});
|
|
571
|
+
});
|