@arkade-os/skill 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.
Files changed (40) hide show
  1. package/README.md +116 -0
  2. package/SKILL.md +269 -0
  3. package/cli/arkade.mjs +1018 -0
  4. package/dist/cjs/index.js +88 -0
  5. package/dist/cjs/index.js.map +1 -0
  6. package/dist/cjs/skills/arkadeBitcoin.js +359 -0
  7. package/dist/cjs/skills/arkadeBitcoin.js.map +1 -0
  8. package/dist/cjs/skills/index.js +78 -0
  9. package/dist/cjs/skills/index.js.map +1 -0
  10. package/dist/cjs/skills/lendaswap.js +458 -0
  11. package/dist/cjs/skills/lendaswap.js.map +1 -0
  12. package/dist/cjs/skills/lightning.js +287 -0
  13. package/dist/cjs/skills/lightning.js.map +1 -0
  14. package/dist/cjs/skills/types.js +11 -0
  15. package/dist/cjs/skills/types.js.map +1 -0
  16. package/dist/esm/index.js +72 -0
  17. package/dist/esm/index.js.map +1 -0
  18. package/dist/esm/skills/arkadeBitcoin.js +354 -0
  19. package/dist/esm/skills/arkadeBitcoin.js.map +1 -0
  20. package/dist/esm/skills/index.js +69 -0
  21. package/dist/esm/skills/index.js.map +1 -0
  22. package/dist/esm/skills/lendaswap.js +453 -0
  23. package/dist/esm/skills/lendaswap.js.map +1 -0
  24. package/dist/esm/skills/lightning.js +282 -0
  25. package/dist/esm/skills/lightning.js.map +1 -0
  26. package/dist/esm/skills/types.js +10 -0
  27. package/dist/esm/skills/types.js.map +1 -0
  28. package/dist/types/index.d.ts +72 -0
  29. package/dist/types/index.d.ts.map +1 -0
  30. package/dist/types/skills/arkadeBitcoin.d.ts +218 -0
  31. package/dist/types/skills/arkadeBitcoin.d.ts.map +1 -0
  32. package/dist/types/skills/index.d.ts +67 -0
  33. package/dist/types/skills/index.d.ts.map +1 -0
  34. package/dist/types/skills/lendaswap.d.ts +152 -0
  35. package/dist/types/skills/lendaswap.d.ts.map +1 -0
  36. package/dist/types/skills/lightning.d.ts +181 -0
  37. package/dist/types/skills/lightning.d.ts.map +1 -0
  38. package/dist/types/skills/types.d.ts +548 -0
  39. package/dist/types/skills/types.d.ts.map +1 -0
  40. package/package.json +65 -0
package/cli/arkade.mjs ADDED
@@ -0,0 +1,1018 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Arkade CLI - Command-line interface for Arkade wallet operations.
5
+ *
6
+ * This CLI is designed for agent integration (CLI-friendly for agents like MoltBot).
7
+ * Data is stored in ~/.arkade-wallet/config.json
8
+ *
9
+ * Default server: https://arkade.computer
10
+ *
11
+ * Usage:
12
+ * arkade generate # Generate random private key
13
+ * arkade init <key> [url] # Initialize wallet
14
+ * arkade address # Show Ark address
15
+ * arkade boarding-address # Show boarding address
16
+ * arkade balance # Show balance breakdown
17
+ * arkade send <address> <amount> # Send sats
18
+ * arkade history # Transaction history
19
+ * arkade onboard # Onchain → Arkade
20
+ * arkade offboard <btc-address> # Arkade → Onchain
21
+ * arkade ln-invoice <amount> [desc] # Create Lightning invoice
22
+ * arkade ln-pay <bolt11> # Pay Lightning invoice
23
+ * arkade ln-fees # Show swap fees
24
+ * arkade ln-limits # Show swap limits
25
+ * arkade ln-pending # Show pending swaps
26
+ * arkade swap-quote <amt> <from> <to> # Get stablecoin quote
27
+ * arkade swap-pairs # Show available pairs
28
+ * arkade help # Show help
29
+ */
30
+
31
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
32
+ import { homedir } from "node:os";
33
+ import { join } from "node:path";
34
+
35
+ const CONFIG_DIR = join(homedir(), ".arkade-wallet");
36
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
37
+ const DEFAULT_SERVER = "https://arkade.computer";
38
+
39
+ /**
40
+ * Load configuration from disk.
41
+ */
42
+ function loadConfig() {
43
+ if (!existsSync(CONFIG_FILE)) {
44
+ return null;
45
+ }
46
+ try {
47
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Save configuration to disk.
55
+ */
56
+ function saveConfig(config) {
57
+ if (!existsSync(CONFIG_DIR)) {
58
+ mkdirSync(CONFIG_DIR, { recursive: true });
59
+ }
60
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
61
+ }
62
+
63
+ /**
64
+ * Get the SDK dynamically.
65
+ */
66
+ async function getSDK() {
67
+ try {
68
+ const sdk = await import("@arkade-os/sdk");
69
+ // Import skills directly (ESM requires explicit .js extensions)
70
+ const { ArkadeBitcoinSkill } = await import(
71
+ "../dist/esm/skills/arkadeBitcoin.js"
72
+ );
73
+ const { ArkaLightningSkill } = await import(
74
+ "../dist/esm/skills/lightning.js"
75
+ );
76
+ const { LendaSwapSkill } = await import("../dist/esm/skills/lendaswap.js");
77
+ return {
78
+ sdk,
79
+ skill: { ArkadeBitcoinSkill, ArkaLightningSkill, LendaSwapSkill },
80
+ };
81
+ } catch (e) {
82
+ console.error("Error: SDK not found. Run 'pnpm build' first.");
83
+ console.error(e.message);
84
+ process.exit(1);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Create wallet from config.
90
+ */
91
+ async function createWallet() {
92
+ const config = loadConfig();
93
+ if (!config) {
94
+ console.error(
95
+ "Error: Wallet not initialized. Run 'arkade init <key>' first.",
96
+ );
97
+ process.exit(1);
98
+ }
99
+
100
+ const { sdk } = await getSDK();
101
+ const { Wallet, SingleKey } = sdk;
102
+
103
+ const wallet = await Wallet.create({
104
+ identity: SingleKey.fromHex(config.privateKey),
105
+ arkServerUrl: config.serverUrl || DEFAULT_SERVER,
106
+ });
107
+
108
+ return wallet;
109
+ }
110
+
111
+ /**
112
+ * Format satoshis for display.
113
+ */
114
+ function formatSats(sats) {
115
+ return sats.toLocaleString();
116
+ }
117
+
118
+ /**
119
+ * Print help message.
120
+ */
121
+ function printHelp() {
122
+ console.log(`
123
+ Arkade CLI - Bitcoin wallet for Arkade and Lightning
124
+
125
+ USAGE:
126
+ arkade <command> [options]
127
+
128
+ COMMANDS:
129
+ generate Generate a new random private key
130
+ init <key> [url] Initialize wallet with private key (hex)
131
+ Default server: arkade.computer
132
+
133
+ address Show Ark address (for receiving offchain)
134
+ boarding-address Show boarding address (for onchain deposits)
135
+ balance Show balance breakdown
136
+
137
+ send <address> <amount> Send satoshis to an Ark address
138
+ history Show transaction history
139
+
140
+ onboard Move funds from onchain to offchain (Arkade)
141
+ offboard <btc-address> Move funds from offchain to onchain
142
+
143
+ ln-invoice <amount> [desc] Create a Lightning invoice
144
+ ln-pay <bolt11> Pay a Lightning invoice
145
+ ln-fees Show Lightning swap fees
146
+ ln-limits Show Lightning swap limits
147
+ ln-pending Show pending Lightning swaps
148
+
149
+ swap-quote <amt> <from> <to> Get stablecoin swap quote
150
+ swap-to-stable <amt> <token> <chain> <evm-addr>
151
+ Swap BTC to stablecoin
152
+ swap-to-btc <amt> <token> <chain> <evm-addr>
153
+ Swap stablecoin to BTC
154
+ swap-status <swap-id> Check swap status
155
+ swap-pending Show pending stablecoin swaps
156
+ swap-pairs Show available stablecoin pairs
157
+
158
+ help Show this help message
159
+
160
+ EXAMPLES:
161
+ arkade generate
162
+ arkade init abc123...
163
+ arkade init abc123... https://custom-server.com
164
+ arkade balance
165
+ arkade send ark1... 50000
166
+ arkade ln-invoice 25000 "Coffee payment"
167
+ arkade ln-pay lnbc...
168
+ arkade swap-to-stable 100000 usdc_pol polygon 0x...
169
+ arkade swap-to-btc 100 usdc_pol polygon 0x...
170
+
171
+ ENVIRONMENT:
172
+ LENDASWAP_API_KEY API key for LendaSwap stablecoin swaps
173
+
174
+ CONFIG:
175
+ Data stored in: ~/.arkade-wallet/config.json
176
+ `);
177
+ }
178
+
179
+ /**
180
+ * Generate a random private key.
181
+ */
182
+ async function cmdGenerate() {
183
+ const { SingleKey } = await import("@arkade-os/sdk");
184
+
185
+ const identity = SingleKey.fromRandomBytes();
186
+ const privateKey = Buffer.from(identity.key).toString("hex");
187
+
188
+ console.log("Generated new private key:");
189
+ console.log(privateKey);
190
+ console.log("");
191
+ console.log("IMPORTANT: Save this key securely! It cannot be recovered.");
192
+ console.log("");
193
+ console.log("To initialize your wallet, run:");
194
+ console.log(` arkade init ${privateKey}`);
195
+ }
196
+
197
+ /**
198
+ * Initialize wallet command.
199
+ */
200
+ async function cmdInit(privateKey, serverUrl) {
201
+ if (!privateKey) {
202
+ console.error("Error: Private key required.");
203
+ console.error("Usage: arkade init <private-key-hex> [server-url]");
204
+ process.exit(1);
205
+ }
206
+
207
+ // Validate private key format
208
+ if (!/^[0-9a-fA-F]{64}$/.test(privateKey)) {
209
+ console.error("Error: Invalid private key. Must be 64 hex characters.");
210
+ process.exit(1);
211
+ }
212
+
213
+ const url = serverUrl || DEFAULT_SERVER;
214
+
215
+ // Test connection
216
+ const { sdk } = await getSDK();
217
+ const { Wallet, SingleKey } = sdk;
218
+
219
+ try {
220
+ const wallet = await Wallet.create({
221
+ identity: SingleKey.fromHex(privateKey),
222
+ arkServerUrl: url,
223
+ });
224
+
225
+ const address = await wallet.getAddress();
226
+
227
+ saveConfig({
228
+ privateKey,
229
+ serverUrl: url,
230
+ createdAt: new Date().toISOString(),
231
+ });
232
+
233
+ console.log("Wallet initialized successfully!");
234
+ console.log(`Server: ${url}`);
235
+ console.log(`Address: ${address}`);
236
+ } catch (e) {
237
+ console.error(`Error: Failed to initialize wallet: ${e.message}`);
238
+ process.exit(1);
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Show Ark address command.
244
+ */
245
+ async function cmdAddress() {
246
+ const wallet = await createWallet();
247
+ const address = await wallet.getAddress();
248
+ console.log(address);
249
+ }
250
+
251
+ /**
252
+ * Show boarding address command.
253
+ */
254
+ async function cmdBoardingAddress() {
255
+ const wallet = await createWallet();
256
+ const address = await wallet.getBoardingAddress();
257
+ console.log(address);
258
+ }
259
+
260
+ /**
261
+ * Show balance command.
262
+ */
263
+ async function cmdBalance() {
264
+ const wallet = await createWallet();
265
+ const { skill } = await getSDK();
266
+ const { ArkadeBitcoinSkill } = skill;
267
+
268
+ const bitcoin = new ArkadeBitcoinSkill(wallet);
269
+ const balance = await bitcoin.getBalance();
270
+
271
+ console.log("Balance Breakdown:");
272
+ console.log("------------------");
273
+ console.log(`Total: ${formatSats(balance.total)} sats`);
274
+ console.log("");
275
+ console.log("Offchain (Ark):");
276
+ console.log(` Available: ${formatSats(balance.offchain.available)} sats`);
277
+ console.log(` Settled: ${formatSats(balance.offchain.settled)} sats`);
278
+ console.log(
279
+ ` Preconfirmed: ${formatSats(balance.offchain.preconfirmed)} sats`,
280
+ );
281
+ console.log(
282
+ ` Recoverable: ${formatSats(balance.offchain.recoverable)} sats`,
283
+ );
284
+ console.log("");
285
+ console.log("Onchain (Boarding):");
286
+ console.log(` Total: ${formatSats(balance.onchain.total)} sats`);
287
+ console.log(` Confirmed: ${formatSats(balance.onchain.confirmed)} sats`);
288
+ console.log(
289
+ ` Unconfirmed: ${formatSats(balance.onchain.unconfirmed)} sats`,
290
+ );
291
+ }
292
+
293
+ /**
294
+ * Send command.
295
+ */
296
+ async function cmdSend(address, amount) {
297
+ if (!address || !amount) {
298
+ console.error("Error: Address and amount required.");
299
+ console.error("Usage: arkade send <address> <amount-sats>");
300
+ process.exit(1);
301
+ }
302
+
303
+ const sats = parseInt(amount, 10);
304
+ if (isNaN(sats) || sats <= 0) {
305
+ console.error("Error: Invalid amount.");
306
+ process.exit(1);
307
+ }
308
+
309
+ const wallet = await createWallet();
310
+ const { skill } = await getSDK();
311
+ const { ArkadeBitcoinSkill } = skill;
312
+
313
+ const bitcoin = new ArkadeBitcoinSkill(wallet);
314
+
315
+ try {
316
+ const result = await bitcoin.send({ address, amount: sats });
317
+ console.log(`Sent ${formatSats(sats)} sats`);
318
+ console.log(`Transaction ID: ${result.txid}`);
319
+ } catch (e) {
320
+ console.error(`Error: ${e.message}`);
321
+ process.exit(1);
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Transaction history command.
327
+ */
328
+ async function cmdHistory() {
329
+ const wallet = await createWallet();
330
+ const { skill } = await getSDK();
331
+ const { ArkadeBitcoinSkill } = skill;
332
+
333
+ const bitcoin = new ArkadeBitcoinSkill(wallet);
334
+ const history = await bitcoin.getTransactionHistory();
335
+
336
+ if (history.length === 0) {
337
+ console.log("No transactions found.");
338
+ return;
339
+ }
340
+
341
+ console.log("Transaction History:");
342
+ console.log("--------------------");
343
+
344
+ for (const tx of history) {
345
+ const type = tx.type === "SENT" ? "SENT" : "RECEIVED";
346
+ const date = new Date(tx.createdAt).toLocaleString();
347
+ const status = tx.settled ? "settled" : "pending";
348
+ console.log(
349
+ `${date} | ${type} | ${formatSats(tx.amount)} sats | ${status}`,
350
+ );
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Onboard command.
356
+ */
357
+ async function cmdOnboard() {
358
+ const wallet = await createWallet();
359
+ const { sdk, skill } = await getSDK();
360
+ const { RestArkProvider } = sdk;
361
+ const { ArkadeBitcoinSkill } = skill;
362
+
363
+ const config = loadConfig();
364
+ const arkProvider = new RestArkProvider(config.serverUrl || DEFAULT_SERVER);
365
+ const arkInfo = await arkProvider.getInfo();
366
+
367
+ const bitcoin = new ArkadeBitcoinSkill(wallet);
368
+ const balance = await bitcoin.getBalance();
369
+
370
+ if (balance.onchain.total === 0) {
371
+ console.log("No boarding UTXOs to onboard.");
372
+ console.log(
373
+ `Send BTC to your boarding address: ${await wallet.getBoardingAddress()}`,
374
+ );
375
+ return;
376
+ }
377
+
378
+ console.log(`Onboarding ${formatSats(balance.onchain.total)} sats...`);
379
+
380
+ try {
381
+ const result = await bitcoin.onboard({
382
+ feeInfo: arkInfo.feeInfo,
383
+ eventCallback: (event) => {
384
+ console.log(` Event: ${event.type}`);
385
+ },
386
+ });
387
+
388
+ console.log(`Onboarded successfully!`);
389
+ console.log(`Commitment TX: ${result.commitmentTxid}`);
390
+ } catch (e) {
391
+ console.error(`Error: ${e.message}`);
392
+ process.exit(1);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Offboard command.
398
+ */
399
+ async function cmdOffboard(destinationAddress) {
400
+ if (!destinationAddress) {
401
+ console.error("Error: Destination address required.");
402
+ console.error("Usage: arkade offboard <btc-address>");
403
+ process.exit(1);
404
+ }
405
+
406
+ const wallet = await createWallet();
407
+ const { sdk, skill } = await getSDK();
408
+ const { RestArkProvider } = sdk;
409
+ const { ArkadeBitcoinSkill } = skill;
410
+
411
+ const config = loadConfig();
412
+ const arkProvider = new RestArkProvider(config.serverUrl || DEFAULT_SERVER);
413
+ const arkInfo = await arkProvider.getInfo();
414
+
415
+ const bitcoin = new ArkadeBitcoinSkill(wallet);
416
+ const balance = await bitcoin.getBalance();
417
+
418
+ if (balance.offchain.available === 0) {
419
+ console.log("No offchain funds to offboard.");
420
+ return;
421
+ }
422
+
423
+ console.log(
424
+ `Offboarding ${formatSats(balance.offchain.available)} sats to ${destinationAddress}...`,
425
+ );
426
+
427
+ try {
428
+ const result = await bitcoin.offboard({
429
+ destinationAddress,
430
+ feeInfo: arkInfo.feeInfo,
431
+ eventCallback: (event) => {
432
+ console.log(` Event: ${event.type}`);
433
+ },
434
+ });
435
+
436
+ console.log(`Offboarded successfully!`);
437
+ console.log(`Commitment TX: ${result.commitmentTxid}`);
438
+ } catch (e) {
439
+ console.error(`Error: ${e.message}`);
440
+ process.exit(1);
441
+ }
442
+ }
443
+
444
+ /**
445
+ * Create Lightning invoice command.
446
+ */
447
+ async function cmdLnInvoice(amount, description) {
448
+ if (!amount) {
449
+ console.error("Error: Amount required.");
450
+ console.error("Usage: arkade ln-invoice <amount-sats> [description]");
451
+ process.exit(1);
452
+ }
453
+
454
+ const sats = parseInt(amount, 10);
455
+ if (isNaN(sats) || sats <= 0) {
456
+ console.error("Error: Invalid amount.");
457
+ process.exit(1);
458
+ }
459
+
460
+ const wallet = await createWallet();
461
+ const { skill } = await getSDK();
462
+ const { ArkaLightningSkill } = skill;
463
+
464
+ const lightning = new ArkaLightningSkill({
465
+ wallet,
466
+ network: "bitcoin",
467
+ });
468
+
469
+ try {
470
+ const invoice = await lightning.createInvoice({
471
+ amount: sats,
472
+ description: description || "Arkade Lightning payment",
473
+ });
474
+
475
+ console.log("Lightning Invoice Created:");
476
+ console.log(`Amount: ${formatSats(invoice.amount)} sats`);
477
+ console.log(`Invoice: ${invoice.bolt11}`);
478
+ console.log(`Payment Hash: ${invoice.paymentHash}`);
479
+ console.log(`Expires in: ${invoice.expirySeconds} seconds`);
480
+ } catch (e) {
481
+ console.error(`Error: ${e.message}`);
482
+ process.exit(1);
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Pay Lightning invoice command.
488
+ */
489
+ async function cmdLnPay(bolt11) {
490
+ if (!bolt11) {
491
+ console.error("Error: Invoice required.");
492
+ console.error("Usage: arkade ln-pay <bolt11-invoice>");
493
+ process.exit(1);
494
+ }
495
+
496
+ const wallet = await createWallet();
497
+ const { skill } = await getSDK();
498
+ const { ArkaLightningSkill } = skill;
499
+
500
+ const lightning = new ArkaLightningSkill({
501
+ wallet,
502
+ network: "bitcoin",
503
+ });
504
+
505
+ console.log("Paying Lightning invoice...");
506
+
507
+ try {
508
+ const result = await lightning.payInvoice({ bolt11 });
509
+
510
+ console.log("Payment successful!");
511
+ console.log(`Amount: ${formatSats(result.amount)} sats`);
512
+ console.log(`Preimage: ${result.preimage}`);
513
+ console.log(`TX ID: ${result.txid}`);
514
+ } catch (e) {
515
+ console.error(`Error: ${e.message}`);
516
+ process.exit(1);
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Show Lightning fees command.
522
+ */
523
+ async function cmdLnFees() {
524
+ const wallet = await createWallet();
525
+ const { skill } = await getSDK();
526
+ const { ArkaLightningSkill } = skill;
527
+
528
+ const lightning = new ArkaLightningSkill({
529
+ wallet,
530
+ network: "bitcoin",
531
+ });
532
+
533
+ try {
534
+ const fees = await lightning.getFees();
535
+
536
+ console.log("Lightning Swap Fees:");
537
+ console.log("--------------------");
538
+ console.log("Submarine Swaps (Pay Invoice):");
539
+ console.log(` Percentage: ${fees.submarine.percentage}%`);
540
+ console.log(` Miner Fee: ${formatSats(fees.submarine.minerFees)} sats`);
541
+ console.log("");
542
+ console.log("Reverse Swaps (Receive Invoice):");
543
+ console.log(` Percentage: ${fees.reverse.percentage}%`);
544
+ console.log(
545
+ ` Lockup Fee: ${formatSats(fees.reverse.minerFees.lockup)} sats`,
546
+ );
547
+ console.log(
548
+ ` Claim Fee: ${formatSats(fees.reverse.minerFees.claim)} sats`,
549
+ );
550
+ } catch (e) {
551
+ console.error(`Error: ${e.message}`);
552
+ process.exit(1);
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Show Lightning limits command.
558
+ */
559
+ async function cmdLnLimits() {
560
+ const wallet = await createWallet();
561
+ const { skill } = await getSDK();
562
+ const { ArkaLightningSkill } = skill;
563
+
564
+ const lightning = new ArkaLightningSkill({
565
+ wallet,
566
+ network: "bitcoin",
567
+ });
568
+
569
+ try {
570
+ const limits = await lightning.getLimits();
571
+
572
+ console.log("Lightning Swap Limits:");
573
+ console.log("----------------------");
574
+ console.log(`Minimum: ${formatSats(limits.min)} sats`);
575
+ console.log(`Maximum: ${formatSats(limits.max)} sats`);
576
+ } catch (e) {
577
+ console.error(`Error: ${e.message}`);
578
+ process.exit(1);
579
+ }
580
+ }
581
+
582
+ /**
583
+ * Show pending Lightning swaps command.
584
+ */
585
+ async function cmdLnPending() {
586
+ const wallet = await createWallet();
587
+ const { skill } = await getSDK();
588
+ const { ArkaLightningSkill } = skill;
589
+
590
+ const lightning = new ArkaLightningSkill({
591
+ wallet,
592
+ network: "bitcoin",
593
+ });
594
+
595
+ try {
596
+ const pending = await lightning.getPendingSwaps();
597
+
598
+ if (pending.length === 0) {
599
+ console.log("No pending swaps.");
600
+ return;
601
+ }
602
+
603
+ console.log("Pending Lightning Swaps:");
604
+ console.log("------------------------");
605
+
606
+ for (const swap of pending) {
607
+ const date = swap.createdAt.toLocaleString();
608
+ console.log(
609
+ `${swap.id} | ${swap.type} | ${formatSats(swap.amount)} sats | ${swap.status} | ${date}`,
610
+ );
611
+ }
612
+ } catch (e) {
613
+ console.error(`Error: ${e.message}`);
614
+ process.exit(1);
615
+ }
616
+ }
617
+
618
+ /**
619
+ * Get stablecoin quote command.
620
+ */
621
+ async function cmdSwapQuote(amount, from, to) {
622
+ if (!amount || !from || !to) {
623
+ console.error("Error: Amount, from, and to required.");
624
+ console.error("Usage: arkade swap-quote <amount> <from> <to>");
625
+ console.error("Example: arkade swap-quote 100000 btc_arkade usdc_pol");
626
+ process.exit(1);
627
+ }
628
+
629
+ const apiKey = process.env.LENDASWAP_API_KEY;
630
+ if (!apiKey) {
631
+ console.error("Error: LENDASWAP_API_KEY environment variable required.");
632
+ process.exit(1);
633
+ }
634
+
635
+ const wallet = await createWallet();
636
+ const { skill } = await getSDK();
637
+ const { LendaSwapSkill } = skill;
638
+
639
+ const lendaswap = new LendaSwapSkill({
640
+ wallet,
641
+ apiKey,
642
+ });
643
+
644
+ try {
645
+ let quote;
646
+ if (from === "btc_arkade" || from === "btc") {
647
+ const sats = parseInt(amount, 10);
648
+ if (isNaN(sats) || sats <= 0) {
649
+ console.error("Error: Invalid amount.");
650
+ process.exit(1);
651
+ }
652
+ quote = await lendaswap.getQuoteBtcToStablecoin(sats, to);
653
+ } else {
654
+ const tokenAmount = parseFloat(amount);
655
+ if (isNaN(tokenAmount) || tokenAmount <= 0) {
656
+ console.error("Error: Invalid amount.");
657
+ process.exit(1);
658
+ }
659
+ quote = await lendaswap.getQuoteStablecoinToBtc(tokenAmount, from);
660
+ }
661
+
662
+ console.log("Swap Quote:");
663
+ console.log("-----------");
664
+ console.log(`From: ${quote.sourceAmount} ${quote.sourceToken}`);
665
+ console.log(`To: ${quote.targetAmount} ${quote.targetToken}`);
666
+ console.log(`Rate: ${quote.exchangeRate}`);
667
+ console.log(`Fee: ${quote.fee.amount} (${quote.fee.percentage}%)`);
668
+ } catch (e) {
669
+ console.error(`Error: ${e.message}`);
670
+ process.exit(1);
671
+ }
672
+ }
673
+
674
+ /**
675
+ * Swap BTC to stablecoin command.
676
+ */
677
+ async function cmdSwapToStable(
678
+ amount,
679
+ targetToken,
680
+ targetChain,
681
+ targetAddress,
682
+ ) {
683
+ if (!amount || !targetToken || !targetChain || !targetAddress) {
684
+ console.error("Error: All parameters required.");
685
+ console.error(
686
+ "Usage: arkade swap-to-stable <amount-sats> <token> <chain> <evm-address>",
687
+ );
688
+ console.error(
689
+ "Example: arkade swap-to-stable 100000 usdc_pol polygon 0x...",
690
+ );
691
+ process.exit(1);
692
+ }
693
+
694
+ const apiKey = process.env.LENDASWAP_API_KEY;
695
+ if (!apiKey) {
696
+ console.error("Error: LENDASWAP_API_KEY environment variable required.");
697
+ process.exit(1);
698
+ }
699
+
700
+ const sats = parseInt(amount, 10);
701
+ if (isNaN(sats) || sats <= 0) {
702
+ console.error("Error: Invalid amount.");
703
+ process.exit(1);
704
+ }
705
+
706
+ const wallet = await createWallet();
707
+ const { skill } = await getSDK();
708
+ const { LendaSwapSkill } = skill;
709
+
710
+ const lendaswap = new LendaSwapSkill({
711
+ wallet,
712
+ apiKey,
713
+ });
714
+
715
+ console.log(`Swapping ${formatSats(sats)} sats to ${targetToken}...`);
716
+
717
+ try {
718
+ const result = await lendaswap.swapBtcToStablecoin({
719
+ sourceAmount: sats,
720
+ targetToken,
721
+ targetChain,
722
+ targetAddress,
723
+ });
724
+
725
+ console.log("Swap created!");
726
+ console.log(`Swap ID: ${result.swapId}`);
727
+ console.log(`Status: ${result.status}`);
728
+ console.log(`Expected: ${result.targetAmount} ${targetToken}`);
729
+ console.log(`Rate: ${result.exchangeRate}`);
730
+ if (result.paymentDetails?.address) {
731
+ console.log(`Payment Address: ${result.paymentDetails.address}`);
732
+ }
733
+ console.log(`Expires: ${result.expiresAt.toLocaleString()}`);
734
+ } catch (e) {
735
+ console.error(`Error: ${e.message}`);
736
+ process.exit(1);
737
+ }
738
+ }
739
+
740
+ /**
741
+ * Swap stablecoin to BTC command.
742
+ */
743
+ async function cmdSwapToBtc(amount, sourceToken, sourceChain, evmAddress) {
744
+ if (!amount || !sourceToken || !sourceChain || !evmAddress) {
745
+ console.error("Error: All parameters required.");
746
+ console.error(
747
+ "Usage: arkade swap-to-btc <amount> <token> <chain> <your-evm-address>",
748
+ );
749
+ console.error("Example: arkade swap-to-btc 100 usdc_pol polygon 0x...");
750
+ process.exit(1);
751
+ }
752
+
753
+ const apiKey = process.env.LENDASWAP_API_KEY;
754
+ if (!apiKey) {
755
+ console.error("Error: LENDASWAP_API_KEY environment variable required.");
756
+ process.exit(1);
757
+ }
758
+
759
+ const tokenAmount = parseFloat(amount);
760
+ if (isNaN(tokenAmount) || tokenAmount <= 0) {
761
+ console.error("Error: Invalid amount.");
762
+ process.exit(1);
763
+ }
764
+
765
+ const wallet = await createWallet();
766
+ const { skill } = await getSDK();
767
+ const { LendaSwapSkill } = skill;
768
+
769
+ const lendaswap = new LendaSwapSkill({
770
+ wallet,
771
+ apiKey,
772
+ });
773
+
774
+ const arkAddress = await wallet.getAddress();
775
+
776
+ console.log(`Swapping ${tokenAmount} ${sourceToken} to BTC...`);
777
+
778
+ try {
779
+ const result = await lendaswap.swapStablecoinToBtc({
780
+ sourceAmount: tokenAmount,
781
+ sourceToken,
782
+ sourceChain,
783
+ targetAddress: arkAddress,
784
+ userAddress: evmAddress,
785
+ });
786
+
787
+ console.log("Swap created!");
788
+ console.log(`Swap ID: ${result.swapId}`);
789
+ console.log(`Status: ${result.status}`);
790
+ console.log(`Expected: ${result.targetAmount} sats`);
791
+ console.log(`Rate: ${result.exchangeRate}`);
792
+ if (result.paymentDetails?.address) {
793
+ console.log(`HTLC Address: ${result.paymentDetails.address}`);
794
+ }
795
+ if (result.paymentDetails?.callData) {
796
+ console.log(`Call Data: ${result.paymentDetails.callData}`);
797
+ }
798
+ console.log(`Expires: ${result.expiresAt.toLocaleString()}`);
799
+ } catch (e) {
800
+ console.error(`Error: ${e.message}`);
801
+ process.exit(1);
802
+ }
803
+ }
804
+
805
+ /**
806
+ * Check swap status command.
807
+ */
808
+ async function cmdSwapStatus(swapId) {
809
+ if (!swapId) {
810
+ console.error("Error: Swap ID required.");
811
+ console.error("Usage: arkade swap-status <swap-id>");
812
+ process.exit(1);
813
+ }
814
+
815
+ const apiKey = process.env.LENDASWAP_API_KEY;
816
+ if (!apiKey) {
817
+ console.error("Error: LENDASWAP_API_KEY environment variable required.");
818
+ process.exit(1);
819
+ }
820
+
821
+ const wallet = await createWallet();
822
+ const { skill } = await getSDK();
823
+ const { LendaSwapSkill } = skill;
824
+
825
+ const lendaswap = new LendaSwapSkill({
826
+ wallet,
827
+ apiKey,
828
+ });
829
+
830
+ try {
831
+ const status = await lendaswap.getSwapStatus(swapId);
832
+
833
+ console.log("Swap Status:");
834
+ console.log("------------");
835
+ console.log(`ID: ${status.id}`);
836
+ console.log(`Direction: ${status.direction}`);
837
+ console.log(`Status: ${status.status}`);
838
+ console.log(`From: ${status.sourceAmount} ${status.sourceToken}`);
839
+ console.log(`To: ${status.targetAmount} ${status.targetToken}`);
840
+ console.log(`Rate: ${status.exchangeRate}`);
841
+ console.log(`Created: ${status.createdAt.toLocaleString()}`);
842
+ if (status.completedAt) {
843
+ console.log(`Completed: ${status.completedAt.toLocaleString()}`);
844
+ }
845
+ if (status.txid) {
846
+ console.log(`TX ID: ${status.txid}`);
847
+ }
848
+ } catch (e) {
849
+ console.error(`Error: ${e.message}`);
850
+ process.exit(1);
851
+ }
852
+ }
853
+
854
+ /**
855
+ * Show pending stablecoin swaps command.
856
+ */
857
+ async function cmdSwapPending() {
858
+ const apiKey = process.env.LENDASWAP_API_KEY;
859
+ if (!apiKey) {
860
+ console.error("Error: LENDASWAP_API_KEY environment variable required.");
861
+ process.exit(1);
862
+ }
863
+
864
+ const wallet = await createWallet();
865
+ const { skill } = await getSDK();
866
+ const { LendaSwapSkill } = skill;
867
+
868
+ const lendaswap = new LendaSwapSkill({
869
+ wallet,
870
+ apiKey,
871
+ });
872
+
873
+ try {
874
+ const pending = await lendaswap.getPendingSwaps();
875
+
876
+ if (pending.length === 0) {
877
+ console.log("No pending swaps.");
878
+ return;
879
+ }
880
+
881
+ console.log("Pending Stablecoin Swaps:");
882
+ console.log("-------------------------");
883
+
884
+ for (const swap of pending) {
885
+ const date = swap.createdAt.toLocaleString();
886
+ console.log(`${swap.id} | ${swap.direction} | ${swap.status} | ${date}`);
887
+ console.log(
888
+ ` ${swap.sourceAmount} ${swap.sourceToken} → ${swap.targetAmount} ${swap.targetToken}`,
889
+ );
890
+ }
891
+ } catch (e) {
892
+ console.error(`Error: ${e.message}`);
893
+ process.exit(1);
894
+ }
895
+ }
896
+
897
+ /**
898
+ * Show available stablecoin pairs command.
899
+ */
900
+ async function cmdSwapPairs() {
901
+ const apiKey = process.env.LENDASWAP_API_KEY;
902
+ if (!apiKey) {
903
+ console.error("Error: LENDASWAP_API_KEY environment variable required.");
904
+ process.exit(1);
905
+ }
906
+
907
+ const wallet = await createWallet();
908
+ const { skill } = await getSDK();
909
+ const { LendaSwapSkill } = skill;
910
+
911
+ const lendaswap = new LendaSwapSkill({
912
+ wallet,
913
+ apiKey,
914
+ });
915
+
916
+ try {
917
+ const pairs = await lendaswap.getAvailablePairs();
918
+
919
+ console.log("Available Trading Pairs:");
920
+ console.log("------------------------");
921
+
922
+ for (const pair of pairs) {
923
+ console.log(`${pair.from} → ${pair.to}`);
924
+ console.log(
925
+ ` Min: ${pair.minAmount} | Max: ${pair.maxAmount} | Fee: ${pair.feePercentage}%`,
926
+ );
927
+ }
928
+ } catch (e) {
929
+ console.error(`Error: ${e.message}`);
930
+ process.exit(1);
931
+ }
932
+ }
933
+
934
+ /**
935
+ * Main entry point.
936
+ */
937
+ async function main() {
938
+ const args = process.argv.slice(2);
939
+ const command = args[0];
940
+
941
+ switch (command) {
942
+ case "generate":
943
+ await cmdGenerate();
944
+ break;
945
+ case "init":
946
+ await cmdInit(args[1], args[2]);
947
+ break;
948
+ case "address":
949
+ await cmdAddress();
950
+ break;
951
+ case "boarding-address":
952
+ await cmdBoardingAddress();
953
+ break;
954
+ case "balance":
955
+ await cmdBalance();
956
+ break;
957
+ case "send":
958
+ await cmdSend(args[1], args[2]);
959
+ break;
960
+ case "history":
961
+ await cmdHistory();
962
+ break;
963
+ case "onboard":
964
+ await cmdOnboard();
965
+ break;
966
+ case "offboard":
967
+ await cmdOffboard(args[1]);
968
+ break;
969
+ case "ln-invoice":
970
+ await cmdLnInvoice(args[1], args.slice(2).join(" "));
971
+ break;
972
+ case "ln-pay":
973
+ await cmdLnPay(args[1]);
974
+ break;
975
+ case "ln-fees":
976
+ await cmdLnFees();
977
+ break;
978
+ case "ln-limits":
979
+ await cmdLnLimits();
980
+ break;
981
+ case "ln-pending":
982
+ await cmdLnPending();
983
+ break;
984
+ case "swap-quote":
985
+ await cmdSwapQuote(args[1], args[2], args[3]);
986
+ break;
987
+ case "swap-to-stable":
988
+ await cmdSwapToStable(args[1], args[2], args[3], args[4]);
989
+ break;
990
+ case "swap-to-btc":
991
+ await cmdSwapToBtc(args[1], args[2], args[3], args[4]);
992
+ break;
993
+ case "swap-status":
994
+ await cmdSwapStatus(args[1]);
995
+ break;
996
+ case "swap-pending":
997
+ await cmdSwapPending();
998
+ break;
999
+ case "swap-pairs":
1000
+ await cmdSwapPairs();
1001
+ break;
1002
+ case "help":
1003
+ case "--help":
1004
+ case "-h":
1005
+ case undefined:
1006
+ printHelp();
1007
+ break;
1008
+ default:
1009
+ console.error(`Unknown command: ${command}`);
1010
+ console.error("Run 'arkade help' for usage.");
1011
+ process.exit(1);
1012
+ }
1013
+ }
1014
+
1015
+ main().catch((e) => {
1016
+ console.error(`Error: ${e.message}`);
1017
+ process.exit(1);
1018
+ });