@ledgerhq/coin-tester-bitcoin 1.1.0-nightly.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.
Files changed (81) hide show
  1. package/.env.example +7 -0
  2. package/.eslintrc.js +23 -0
  3. package/.turbo/turbo-build.log +4 -0
  4. package/.unimportedrc.json +8 -0
  5. package/CHANGELOG.md +18 -0
  6. package/LICENSE.txt +21 -0
  7. package/README.md +30 -0
  8. package/jest.config.ts +19 -0
  9. package/lib/src/assert.d.ts +6 -0
  10. package/lib/src/assert.d.ts.map +1 -0
  11. package/lib/src/assert.js +29 -0
  12. package/lib/src/assert.js.map +1 -0
  13. package/lib/src/atlas.d.ts +3 -0
  14. package/lib/src/atlas.d.ts.map +1 -0
  15. package/lib/src/atlas.js +84 -0
  16. package/lib/src/atlas.js.map +1 -0
  17. package/lib/src/constants.d.ts +5 -0
  18. package/lib/src/constants.d.ts.map +1 -0
  19. package/lib/src/constants.js +5 -0
  20. package/lib/src/constants.js.map +1 -0
  21. package/lib/src/fixtures.d.ts +5 -0
  22. package/lib/src/fixtures.d.ts.map +1 -0
  23. package/lib/src/fixtures.js +61 -0
  24. package/lib/src/fixtures.js.map +1 -0
  25. package/lib/src/helpers.d.ts +11 -0
  26. package/lib/src/helpers.d.ts.map +1 -0
  27. package/lib/src/helpers.js +339 -0
  28. package/lib/src/helpers.js.map +1 -0
  29. package/lib/src/scenarii/bitcoin.d.ts +4 -0
  30. package/lib/src/scenarii/bitcoin.d.ts.map +1 -0
  31. package/lib/src/scenarii/bitcoin.js +258 -0
  32. package/lib/src/scenarii/bitcoin.js.map +1 -0
  33. package/lib/src/utils.d.ts +5 -0
  34. package/lib/src/utils.d.ts.map +1 -0
  35. package/lib/src/utils.js +48 -0
  36. package/lib/src/utils.js.map +1 -0
  37. package/lib/tsconfig.tsbuildinfo +1 -0
  38. package/lib-es/src/assert.d.ts +6 -0
  39. package/lib-es/src/assert.d.ts.map +1 -0
  40. package/lib-es/src/assert.js +23 -0
  41. package/lib-es/src/assert.js.map +1 -0
  42. package/lib-es/src/atlas.d.ts +3 -0
  43. package/lib-es/src/atlas.d.ts.map +1 -0
  44. package/lib-es/src/atlas.js +43 -0
  45. package/lib-es/src/atlas.js.map +1 -0
  46. package/lib-es/src/constants.d.ts +5 -0
  47. package/lib-es/src/constants.d.ts.map +1 -0
  48. package/lib-es/src/constants.js +2 -0
  49. package/lib-es/src/constants.js.map +1 -0
  50. package/lib-es/src/fixtures.d.ts +5 -0
  51. package/lib-es/src/fixtures.d.ts.map +1 -0
  52. package/lib-es/src/fixtures.js +54 -0
  53. package/lib-es/src/fixtures.js.map +1 -0
  54. package/lib-es/src/helpers.d.ts +11 -0
  55. package/lib-es/src/helpers.d.ts.map +1 -0
  56. package/lib-es/src/helpers.js +323 -0
  57. package/lib-es/src/helpers.js.map +1 -0
  58. package/lib-es/src/scenarii/bitcoin.d.ts +4 -0
  59. package/lib-es/src/scenarii/bitcoin.d.ts.map +1 -0
  60. package/lib-es/src/scenarii/bitcoin.js +252 -0
  61. package/lib-es/src/scenarii/bitcoin.js.map +1 -0
  62. package/lib-es/src/utils.d.ts +5 -0
  63. package/lib-es/src/utils.d.ts.map +1 -0
  64. package/lib-es/src/utils.js +40 -0
  65. package/lib-es/src/utils.js.map +1 -0
  66. package/lib-es/tsconfig.tsbuildinfo +1 -0
  67. package/package.json +83 -0
  68. package/src/assert.ts +34 -0
  69. package/src/atlas.ts +52 -0
  70. package/src/constants.ts +1 -0
  71. package/src/docker/atlas/bitcoin.conf +56 -0
  72. package/src/docker/atlas/pending.conf +36 -0
  73. package/src/docker/docker-compose.yml +76 -0
  74. package/src/docker/nginx/default.conf +31 -0
  75. package/src/docker/postgres/create-db.sh +9 -0
  76. package/src/fixtures.ts +66 -0
  77. package/src/helpers.ts +372 -0
  78. package/src/scenarii/bitcoin.ts +306 -0
  79. package/src/scenarii.test.ts +27 -0
  80. package/src/utils.ts +52 -0
  81. package/tsconfig.json +14 -0
@@ -0,0 +1,76 @@
1
+ services:
2
+ postgres:
3
+ image: postgres:15
4
+ cpus: 1
5
+ environment:
6
+ POSTGRES_USER: username
7
+ POSTGRES_PASSWORD: password
8
+ volumes:
9
+ - "./postgres:/docker-entrypoint-initdb.d"
10
+ healthcheck:
11
+ test: ["CMD-SHELL", "pg_isready -U username"]
12
+ interval: 10s
13
+ timeout: 5s
14
+ retries: 5
15
+ btcliked:
16
+ image: jfrog.ledgerlabs.net/bbs-oci-prod-green/bitcoin-node:28.0
17
+ platform: linux/amd64
18
+ command: /app/bitcoin/bin/bitcoind -datadir=/config -regtest -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0 -rpcuser=user -rpcpassword=pass -fallbackfee=0.001 -deprecatedrpc=addresses -txindex -zmqpubsequence=tcp://0.0.0.0:19999
19
+ ports:
20
+ - "18443:18443" # exposes RPC
21
+ atlas-pending:
22
+ image: jfrog.ledgerlabs.net/bbs-oci-prod-green/atlas-bitcoin-pending-v2:rolling
23
+ platform: linux/amd64
24
+ cpus: 1
25
+ pull_policy: always
26
+ command: /app/bin/atlas /config/pending.conf
27
+ volumes:
28
+ - "./atlas:/config"
29
+ depends_on:
30
+ rabbitmq:
31
+ condition: service_healthy
32
+ btcliked:
33
+ condition: service_started
34
+ restart: on-failure
35
+ healthcheck:
36
+ test: curl --fail http://localhost:9877/_health || exit 1
37
+ interval: 10s
38
+ retries: 5
39
+ timeout: 5s
40
+ atlas:
41
+ image: jfrog.ledgerlabs.net/bbs-oci-prod-green/atlas-bitcoin-v2:rolling
42
+ platform: linux/amd64
43
+ cpus: 1
44
+ pull_policy: always
45
+ command: /app/bin/atlas /config/bitcoin.conf
46
+ volumes:
47
+ - "./atlas:/config"
48
+ depends_on:
49
+ postgres:
50
+ condition: service_healthy
51
+ atlas-pending:
52
+ condition: service_started
53
+ btcliked:
54
+ condition: service_started
55
+ restart: on-failure
56
+ healthcheck:
57
+ test: curl --fail http://localhost:9877/_health || exit 1
58
+ interval: 10s
59
+ retries: 5
60
+ timeout: 5s
61
+ rabbitmq:
62
+ cpus: 1
63
+ image: rabbitmq:3
64
+ healthcheck:
65
+ test: rabbitmq-diagnostics -q ping
66
+ interval: 5s
67
+ timeout: 30s
68
+ retries: 10
69
+ atlas-nginx:
70
+ image: nginx:latest
71
+ volumes:
72
+ - "./nginx:/etc/nginx/conf.d/"
73
+ depends_on:
74
+ - atlas
75
+ ports:
76
+ - "9876:9876"
@@ -0,0 +1,31 @@
1
+ upstream atlas {
2
+ server atlas:9876;
3
+ keepalive 20;
4
+ }
5
+ upstream atlas-pending {
6
+ server atlas-pending:9876;
7
+ keepalive 20;
8
+ }
9
+ server {
10
+ listen 9876;
11
+ listen [::]:9876;
12
+ server_name localhost;
13
+
14
+ location / {
15
+ proxy_http_version 1.1;
16
+ proxy_set_header Host $host;
17
+ proxy_set_header X-Real-IP $remote_addr;
18
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
19
+ proxy_set_header X-Forwarded-Proto $scheme;
20
+ proxy_set_header Connection "";
21
+ proxy_read_timeout 300;
22
+ proxy_buffering off;
23
+
24
+ proxy_pass http://atlas; # NOTE: no trailing slash -> preserves the full incoming path
25
+ }
26
+
27
+ error_page 500 502 503 504 /50x.html;
28
+ location = /50x.html {
29
+ root /usr/share/nginx/html;
30
+ }
31
+ }
@@ -0,0 +1,9 @@
1
+ dbs=(
2
+ atlas-bitcoin
3
+ a4-bitcoin
4
+ )
5
+
6
+ for db in "${dbs[@]}"; do
7
+ echo "Creating database $db"
8
+ createdb $db -w -U username
9
+ done
@@ -0,0 +1,66 @@
1
+ /* instanbul ignore file: don't test fixtures */
2
+
3
+ import BigNumber from "bignumber.js";
4
+ import { getDerivationScheme, runDerivationScheme } from "@ledgerhq/coin-framework/derivation";
5
+ import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
6
+ import { DerivationMode } from "@ledgerhq/types-live";
7
+ import { BitcoinAccount } from "@ledgerhq/coin-bitcoin/types";
8
+
9
+ export const makeAccount = (
10
+ xpub: string,
11
+ publicKey: string,
12
+ address: string,
13
+ currency: CryptoCurrency,
14
+ derivationMode: DerivationMode,
15
+ ): BitcoinAccount => {
16
+ const id = `js:2:${currency.id}:${xpub}:${derivationMode}`;
17
+ const scheme = getDerivationScheme({
18
+ derivationMode: derivationMode as DerivationMode,
19
+ currency,
20
+ });
21
+ const index = 0;
22
+ const freshAddressPath = runDerivationScheme(scheme, currency, {
23
+ account: index,
24
+ node: 0,
25
+ address: 0,
26
+ });
27
+
28
+ return {
29
+ type: "Account",
30
+ id: id,
31
+ seedIdentifier: publicKey,
32
+ derivationMode: derivationMode,
33
+ index: 0,
34
+ freshAddress: address,
35
+ freshAddressPath: freshAddressPath,
36
+ used: true,
37
+ balance: new BigNumber(0),
38
+ spendableBalance: new BigNumber(0),
39
+ creationDate: new Date(),
40
+ blockHeight: 0,
41
+ currency,
42
+ operationsCount: 0,
43
+ operations: [],
44
+ pendingOperations: [],
45
+ lastSyncDate: new Date(),
46
+ balanceHistoryCache: {
47
+ HOUR: {
48
+ latestDate: null,
49
+ balances: [],
50
+ },
51
+ DAY: {
52
+ latestDate: null,
53
+ balances: [],
54
+ },
55
+ WEEK: {
56
+ latestDate: null,
57
+ balances: [],
58
+ },
59
+ },
60
+ swapHistory: [],
61
+
62
+ bitcoinResources: {
63
+ utxos: [],
64
+ },
65
+ };
66
+ };
package/src/helpers.ts ADDED
@@ -0,0 +1,372 @@
1
+ import Client from "bitcoin-core";
2
+
3
+ // fetched from bitcoin.conf
4
+ const client: any = new Client({
5
+ version: "0.24.1",
6
+ username: "user",
7
+ password: "pass",
8
+ host: "http://localhost:18443",
9
+ });
10
+
11
+ export async function loadWallet(name: string): Promise<void> {
12
+ try {
13
+ // Try creating and loading the wallet
14
+ const res = await client.command("createwallet", name);
15
+ console.log(`✅ Wallet "${name}" created and loaded:`, res);
16
+
17
+ // Optionally verify that the wallet is accessible
18
+ await client.getBalance({ minconf: 0 });
19
+ return;
20
+ } catch (error: any) {
21
+ const message = error?.message || "Unknown error";
22
+
23
+ // Wallet already exists → try loading it
24
+ if (message.includes("already exists")) {
25
+ console.log(`ℹ️ Wallet "${name}" already exists. Attempting to load...`);
26
+ return loadExistingWallet(name);
27
+ }
28
+
29
+ console.error(`❌ Failed to create wallet "${name}":`, message);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Attempt to load an existing wallet, handling "already loaded" gracefully.
35
+ */
36
+ async function loadExistingWallet(name: string): Promise<void> {
37
+ try {
38
+ await client.command("loadwallet", name);
39
+ console.log(`✅ Wallet "${name}" loaded successfully.`);
40
+ } catch (error: any) {
41
+ const message = error?.message || "Unknown error";
42
+
43
+ if (message.includes("already loaded")) {
44
+ console.log(`ℹ️ Wallet "${name}" is already loaded.`);
45
+ } else {
46
+ console.error(`❌ Failed to load existing wallet "${name}":`, message);
47
+ }
48
+ }
49
+ }
50
+
51
+ export async function mineToWalletAddress(param: string) {
52
+ const address = await client.getNewAddress();
53
+ const nbBlocks = parseInt(param);
54
+ await client.generateToAddress({
55
+ nblocks: nbBlocks,
56
+ address,
57
+ });
58
+ console.log(`Mined ${nbBlocks} blocks to: ${address}`);
59
+ }
60
+ const FEES = 0.0001;
61
+
62
+ export const mineBlock = async (address: string) => {
63
+ await client.generateToAddress({
64
+ nblocks: 1,
65
+ address,
66
+ });
67
+ };
68
+
69
+ export const getCurrentBlock = async () => {
70
+ return await client.command("getblockcount");
71
+ };
72
+
73
+ /*
74
+ * Note that it sets the input sequence to 4294967293, <=0xFFFFFFFD — Replace By Fee (RBF).
75
+ */
76
+ export const sendTo = async (recipientAddress: string, amount: number) => {
77
+ await client.getBalance({ minconf: 0 });
78
+ if (!recipientAddress || amount <= 0) {
79
+ console.error("Invalid parameters: Provide a valid recipient address and positive amount.");
80
+ return;
81
+ }
82
+
83
+ console.log(`Sending ${amount} BTC to ${recipientAddress}...`);
84
+
85
+ try {
86
+ // Step 1: Send the specified amount to the recipient address
87
+ const txSendToAddress = await client
88
+ .sendToAddress({
89
+ address: recipientAddress,
90
+ amount: amount,
91
+ })
92
+ .then((txid: string) => txid);
93
+
94
+ // Step 2: Fetch transaction details
95
+ await client.getTransaction({
96
+ txid: txSendToAddress,
97
+ verbose: true,
98
+ });
99
+
100
+ // Step 4: Fetch updated transaction details after confirmation
101
+ await client.getTransaction({
102
+ txid: txSendToAddress,
103
+ verbose: true,
104
+ });
105
+ } catch (error: any) {
106
+ console.error("Error in sendToAddress:", error.message);
107
+ }
108
+ };
109
+ export const sendToMany = async (recipientAddress: string, amount: number, times: number) => {
110
+ // calls sendTo times times
111
+ for (let i = 0; i < times; i++) {
112
+ await sendTo(recipientAddress, amount);
113
+ }
114
+ };
115
+
116
+ export const sendAutomatedRaw = async (destinationAddress: string, amount: number) => {
117
+ if (!destinationAddress || amount <= 0) {
118
+ console.error("Invalid parameters: Provide a valid address and positive amount.");
119
+ return;
120
+ }
121
+
122
+ try {
123
+ // Step 1: Create an unfinished raw transaction (no inputs, outputs only)
124
+ const unfinishedTx = await client.createRawTransaction({
125
+ inputs: [],
126
+ outputs: {
127
+ [destinationAddress]: amount,
128
+ },
129
+ });
130
+
131
+ // Step 2: Fund the transaction (Bitcoin Core automatically selects UTXOs)
132
+ const fundedTx = await client.fundRawTransaction({
133
+ hexstring: unfinishedTx,
134
+ options: { replaceable: true }, // Set to true if RBF is needed
135
+ // if replaceable: false, sequence of vin set to 4294967294 (0xFFFFFFFE) // Locktime BUT non-rbf
136
+ // else, sets to: 4294967293 // Locktime & RBF
137
+ });
138
+
139
+ // Step 3: Decode the transaction for debugging
140
+ await client.decodeRawTransaction({
141
+ hexstring: fundedTx.hex,
142
+ });
143
+
144
+ // Step 4: Sign the transaction
145
+ const signedTxHex = await client.signRawTransactionWithWallet({
146
+ hexstring: fundedTx.hex,
147
+ });
148
+
149
+ // Step 5: Broadcast the transaction to the network
150
+ const transactionId = await client.sendRawTransaction({
151
+ hexstring: signedTxHex.hex,
152
+ });
153
+
154
+ console.log(`Transaction Broadcasted! TXID: ${transactionId}`);
155
+ } catch (error: any) {
156
+ console.error("Error sending transaction:", error.message);
157
+ }
158
+ };
159
+
160
+ /*
161
+ * https://learnmeabitcoin.com/technical/transaction/input/sequence/
162
+ * NOTE: You only need to set one of the sequence fields to enable locktime or RBF
163
+ * (even if you have multiple inputs and sequence fields in one transaction)
164
+ * However, relative locktime settings are specific to each input.
165
+ *
166
+ * If set to 0xFFFFFFFE (4294967294) → Locktime / Non-RBF
167
+ * If set to a number ≤ 0x0000FFFF (65535) → Blocks-based timelock.
168
+ * Sequence Effect
169
+ * 0xFFFFFFFE (4294967294) Default (Non-RBF): Cannot be replaced
170
+ * 0xFFFFFFFD (4294967293) Opt-in RBF: Can be replaced by a higher fee transaction
171
+ *
172
+ *
173
+ * Called like this as you've got more control over all the inputs
174
+ */
175
+ export const sendRaw = async (recipientAddress: string, amount: number, sequence = 4294967294) => {
176
+ if (!recipientAddress || amount <= 0) {
177
+ console.error("Invalid parameters: Provide a valid address and positive amount.");
178
+ return;
179
+ }
180
+
181
+ console.log(
182
+ `Creating raw transaction: Sending ${amount} BTC to ${recipientAddress} with sequence=${sequence}...`,
183
+ );
184
+
185
+ try {
186
+ // Step 2: Get new addresses
187
+ const newAddress = await client.getNewAddress({ address_type: "legacy" });
188
+ const changeAddress = await client.getRawChangeAddress({
189
+ address_type: "legacy",
190
+ });
191
+
192
+ // Step 3: Get UTXO for spending
193
+ const unspents = await client.listUnspent({});
194
+ let id = 0;
195
+ let unspent = unspents[id];
196
+
197
+ while (unspent.amount < amount) {
198
+ console.warn("Insufficient funds. Picking another UTXO.");
199
+ id++;
200
+ unspent = unspents[id];
201
+ }
202
+
203
+ // Step 4: Calculate change
204
+ const changeAmountStr = Number(unspent.amount - amount - FEES).toFixed(6);
205
+ const changeAmount = Number(changeAmountStr);
206
+
207
+ if (changeAmount < 0) {
208
+ console.warn("Insufficient funds after fees.");
209
+ return;
210
+ }
211
+
212
+ // Step 5: Create raw transaction
213
+ const rawTxHex = await client.createRawTransaction({
214
+ inputs: [
215
+ {
216
+ txid: unspent.txid,
217
+ vout: unspent.vout,
218
+ sequence: sequence,
219
+ },
220
+ ],
221
+ outputs: {
222
+ [recipientAddress]: amount,
223
+ [changeAddress]: changeAmount,
224
+ },
225
+ });
226
+
227
+ await client.decodeRawTransaction({
228
+ hexstring: rawTxHex,
229
+ });
230
+
231
+ // Step 6: Sign the transaction
232
+ const signedTxHex = await client.signRawTransactionWithWallet({
233
+ hexstring: rawTxHex,
234
+ });
235
+
236
+ // Step 7: Broadcast the transaction
237
+ const txId = await client.sendRawTransaction({
238
+ hexstring: signedTxHex.hex,
239
+ });
240
+
241
+ // Step 8: Fetch transaction details
242
+ await client.getTransaction({ txid: txId, verbose: true });
243
+
244
+ // Step 9: Mine a block to confirm the transaction
245
+ await mineBlock(newAddress);
246
+ } catch (error: any) {
247
+ console.error("Error in sendRaw:", error.message);
248
+ console.error({ error });
249
+ }
250
+ };
251
+
252
+ export const sendToReplaceCurrentTx = async (recipientAddress: string, amount: number) => {
253
+ if (!recipientAddress || amount <= 0) {
254
+ console.error("Invalid parameters: Provide a valid address and positive amount.");
255
+ return;
256
+ }
257
+
258
+ console.log(
259
+ `Sending replaceable transaction to ${recipientAddress} with amount ${amount} BTC...`,
260
+ );
261
+
262
+ try {
263
+ // Step 1: Get an unspent UTXO
264
+ const unspent = await client.listUnspent({
265
+ query_options: { minimumSumAmount: amount },
266
+ });
267
+
268
+ if (!unspent.length) {
269
+ console.error("No suitable UTXOs available.");
270
+ return;
271
+ }
272
+
273
+ const utxo = unspent[0];
274
+
275
+ // Step 2: Create the replaceable transaction (RBF enabled)
276
+ const rawTx1 = await client.createRawTransaction({
277
+ inputs: [
278
+ {
279
+ txid: utxo.txid,
280
+ vout: utxo.vout,
281
+ sequence: 4294967293, // RBF enabled
282
+ },
283
+ ],
284
+ outputs: {
285
+ [recipientAddress]: amount,
286
+ },
287
+ });
288
+
289
+ // Step 3: Fund & Sign the transaction
290
+ const fundedTx1 = await client.fundRawTransaction({
291
+ hexstring: rawTx1,
292
+ options: { feeRate: 0.0004 }, // Increase fee rate
293
+ });
294
+ const signedTx1 = await client.signRawTransactionWithWallet({
295
+ hexstring: fundedTx1.hex,
296
+ });
297
+
298
+ // Step 4: Broadcast the transaction
299
+ const txId1 = await client.sendRawTransaction({ hexstring: signedTx1.hex });
300
+ console.log(`Transaction sent (TXID: ${txId1}), waiting before replacing...`);
301
+ console.log(`If you need to, make a tx that sends funds to ${recipientAddress}`);
302
+ } catch (error: any) {
303
+ console.error("Error in sendReplaceableTransaction:", error.message);
304
+ }
305
+ };
306
+
307
+ export const sendReplaceableTransaction = async (recipientAddress: string, amount: number) => {
308
+ if (!recipientAddress || amount <= 0) {
309
+ console.error("Invalid parameters: Provide a valid address and positive amount.");
310
+ return;
311
+ }
312
+
313
+ console.log(
314
+ `Sending replaceable transaction to ${recipientAddress} with amount ${amount} BTC...`,
315
+ );
316
+
317
+ try {
318
+ // Step 1: Get an unspent UTXO
319
+ const unspent = await client.listUnspent({
320
+ query_options: { minimumSumAmount: amount },
321
+ });
322
+
323
+ if (!unspent.length) {
324
+ console.error("No suitable UTXOs available.");
325
+ return;
326
+ }
327
+
328
+ const utxo = unspent[0];
329
+
330
+ // Step 2: Create the first replaceable transaction (RBF enabled)
331
+ const rawTx1 = await client.createRawTransaction({
332
+ inputs: [
333
+ {
334
+ txid: utxo.txid,
335
+ vout: utxo.vout,
336
+ sequence: 4294967293, // RBF enabled
337
+ },
338
+ ],
339
+ outputs: {
340
+ [recipientAddress]: amount,
341
+ },
342
+ });
343
+
344
+ // Step 3: Fund & Sign the first transaction
345
+ const fundedTx1 = await client.fundRawTransaction({
346
+ hexstring: rawTx1,
347
+ options: { feeRate: 0.0002 }, // Increase fee rate
348
+ });
349
+ const signedTx1 = await client.signRawTransactionWithWallet({
350
+ hexstring: fundedTx1.hex,
351
+ });
352
+
353
+ // Step 4: Broadcast the first transaction
354
+ const txId1 = await client.sendRawTransaction({ hexstring: signedTx1.hex });
355
+ console.log(`First transaction sent (TXID: ${txId1}), waiting before replacing...`);
356
+ } catch (error: any) {
357
+ console.error("Error in sendReplaceableTransaction:", error.message);
358
+ }
359
+ };
360
+
361
+ /*Available commands:
362
+ mineBlock <address> - Mine a block to the address
363
+ sendTo <address> <amount> - Send a transaction to an address
364
+ sendToMany <address> <amount> <times> - Send a transaction to an address
365
+ replaceTx <address> <amount> - Send a transaction and replace it using RBF
366
+ sendAutomatedRaw <address> <amount> - Send a raw transaction, automatically funded
367
+ sendRaw <address> <amount> [sequence] - Send a raw transaction with a custom sequence
368
+ sendRawTwoOutputs <address1> <address2> <amount> [sequence] - Send a raw transaction with a custom sequence to 2 addresses
369
+ # doubleSpend <address> - Attempt a double-spend attack
370
+ # dustTransaction <address> - Create a dust transaction
371
+ # multisig <address> - Test a multisig transaction`;
372
+ */