types.ts raw
1 import {
2 BellIcon,
3 CirclePlusIcon,
4 CrownIcon,
5 HandCoinsIcon,
6 InfoIcon,
7 LucideIcon,
8 NotebookTabsIcon,
9 PenLineIcon,
10 SearchIcon,
11 WalletMinimalIcon,
12 } from "lucide-react";
13
14 export type BackendType = "LND" | "LDK" | "PHOENIX" | "CASHU" | "CLN" | "BARK";
15
16 export type Nip47RequestMethod =
17 | "get_info"
18 | "get_balance"
19 | "get_budget"
20 | "make_invoice"
21 | "pay_invoice"
22 | "pay_keysend"
23 | "lookup_invoice"
24 | "list_transactions"
25 | "sign_message"
26 | "multi_pay_invoice"
27 | "multi_pay_keysend"
28 | "make_hold_invoice"
29 | "settle_hold_invoice"
30 | "cancel_hold_invoice";
31
32 export type BudgetRenewalType =
33 | "daily"
34 | "weekly"
35 | "monthly"
36 | "yearly"
37 | "never"
38 | "";
39
40 export type Scope =
41 | "pay_invoice" // also used for pay_keysend, multi_pay_invoice, multi_pay_keysend
42 | "get_balance"
43 | "get_info"
44 | "make_invoice"
45 | "lookup_invoice"
46 | "list_transactions"
47 | "sign_message"
48 | "notifications" // covers all notification types
49 | "superuser";
50
51 export type Nip47NotificationType = "payment_received" | "payment_sent";
52
53 export type ScopeIconMap = {
54 [key in Scope]: LucideIcon;
55 };
56
57 export const scopeIconMap: ScopeIconMap = {
58 get_balance: WalletMinimalIcon,
59 get_info: InfoIcon,
60 list_transactions: NotebookTabsIcon,
61 lookup_invoice: SearchIcon,
62 make_invoice: CirclePlusIcon,
63 pay_invoice: HandCoinsIcon,
64 sign_message: PenLineIcon,
65 notifications: BellIcon,
66 superuser: CrownIcon,
67 };
68
69 export type WalletCapabilities = {
70 methods: Nip47RequestMethod[];
71 scopes: Scope[];
72 notificationTypes: Nip47NotificationType[];
73 };
74
75 export const validBudgetRenewals: BudgetRenewalType[] = [
76 "daily",
77 "weekly",
78 "monthly",
79 "yearly",
80 "never",
81 ];
82
83 // Scopes granted to a read-only connection: receive payments and view
84 // balance/history, but never send/spend.
85 export const READ_ONLY_SCOPES: Scope[] = [
86 "get_balance",
87 "get_info",
88 "make_invoice",
89 "lookup_invoice",
90 "list_transactions",
91 "notifications",
92 ];
93
94 export const scopeDescriptions: Record<Scope, string> = {
95 get_balance: "Read your balance",
96 get_info: "Read your node info",
97 list_transactions: "Read transaction history",
98 lookup_invoice: "Lookup status of invoices",
99 make_invoice: "Create invoices",
100 pay_invoice: "Send payments",
101 sign_message: "Sign messages",
102 notifications: "Receive wallet notifications",
103 superuser: "Create other app connections",
104 };
105
106 export const expiryOptions: Record<string, number> = {
107 "1 week": 7,
108 "1 month": 30,
109 "1 year": 365,
110 };
111
112 export const budgetOptionsSat: Record<string, number> = {
113 "10k": 10_000,
114 "100k": 100_000,
115 "1M": 1_000_000,
116 };
117
118 export interface ErrorResponse {
119 message: string;
120 }
121
122 export interface App {
123 id: number;
124 name: string;
125 description: string;
126 appPubkey: string;
127 uniqueWalletPubkey: boolean;
128 walletPubkey: string;
129 createdAt: string;
130 updatedAt: string;
131 lastUsedAt?: string;
132 lastSettledTransactionAt?: string;
133 expiresAt?: string;
134 isolated: boolean;
135 balanceSat: number;
136 balanceMsat: number;
137
138 scopes: Scope[];
139 maxAmountSat: number;
140 maxAmountMsat: number;
141 budgetUsageSat: number;
142 budgetUsageMsat: number;
143 budgetRenewal: BudgetRenewalType;
144 metadata?: AppMetadata;
145 }
146
147 export interface AppPermissions {
148 scopes: Scope[];
149 maxAmountSat: number;
150 budgetRenewal: BudgetRenewalType;
151 expiresAt?: Date;
152 isolated: boolean;
153 }
154
155 export interface InfoResponse {
156 backendType: BackendType;
157 setupCompleted: boolean;
158 oauthRedirect: boolean;
159 albyAccountConnected: boolean;
160 ldkVssEnabled: boolean;
161 ldkVssUrl: string;
162 vssSupported: boolean;
163 databaseType: string;
164 running: boolean;
165 albyAuthUrl: string;
166 nextBackupReminder: string;
167 albyUserIdentifier: string;
168 network?: Network;
169 version: string;
170 relays: { url: string; online: boolean }[];
171 unlocked: boolean;
172 enableAdvancedSetup: boolean;
173 startupState: string;
174 startupError: string;
175 startupErrorTime: string;
176 autoUnlockPasswordSupported: boolean;
177 autoUnlockPasswordEnabled: boolean;
178 currency: string;
179 nodeAlias: string;
180 mempoolUrl: string;
181 bitcoinDisplayFormat: BitcoinDisplayFormat;
182 chainDataSourceType?: string;
183 chainDataSourceAddress?: string;
184 jitChannelsLiquiditySource?: string;
185 jitChannelsMinPaymentSizeMsat?: number;
186 jitChannelsMaxPaymentSizeMsat?: number;
187 jitChannelsEnabled: boolean;
188 hideUpdateBanner: boolean;
189 supportsBolt12: boolean;
190 nodeMigrationFileCreated: boolean;
191 }
192
193 export type BitcoinDisplayFormat = "sats" | "bip177";
194
195 export type HealthAlarmKind =
196 | "alby_service"
197 | "node_not_ready"
198 | "channels_offline"
199 | "nostr_relay_offline"
200 | "vss_no_subscription";
201
202 export type HealthAlarm = {
203 kind: HealthAlarmKind;
204 rawDetails?: unknown;
205 };
206 export type AlbyInfoIncident = {
207 name: string;
208 started: string;
209 status: string;
210 impact: string;
211 url: string;
212 };
213
214 export type HealthResponse = {
215 alarms: HealthAlarm[];
216 };
217
218 export type Network = "bitcoin" | "testnet" | "signet";
219
220 export type AppMetadata = {
221 app_store_app_id?: string;
222 lud16?: string;
223 } & Record<string, unknown>;
224
225 export type AutoSwapConfig = {
226 type: "out";
227 enabled: boolean;
228 balanceThresholdSat: number;
229 swapAmountSat: number;
230 destination: string;
231 };
232
233 export type SwapInfo = {
234 albyServiceFee: number;
235 boltzServiceFee: number;
236 boltzNetworkFeeSat: number;
237 minAmountSat: number;
238 maxAmountSat: number;
239 };
240
241 export type BaseSwap = {
242 id: string;
243 sendAmountSat: number;
244 lockupAddress: string;
245 paymentHash: string;
246 invoice: string;
247 autoSwap: boolean;
248 usedXpub: boolean;
249 boltzPubkey: string;
250 createdAt: string;
251 updatedAt: string;
252 lockupTxId?: string;
253 claimTxId?: string;
254 receiveAmountSat?: number;
255 };
256
257 export type SwapIn = BaseSwap & {
258 type: "in";
259 state: "PENDING" | "SUCCESS" | "FAILED" | "REFUNDED";
260 refundAddress?: string;
261 };
262
263 export type SwapOut = BaseSwap & {
264 type: "out";
265 state: "PENDING" | "SUCCESS" | "FAILED";
266 destinationAddress: string;
267 };
268
269 export type Swap = SwapIn | SwapOut;
270
271 export type SwapResponse = {
272 swapId: string;
273 paymentHash: string;
274 };
275
276 export interface MnemonicResponse {
277 mnemonic: string;
278 }
279
280 export interface CreateAppRequest {
281 name: string;
282 pubkey?: string;
283 maxAmountSat?: number;
284 maxAmountMsat?: number;
285 budgetRenewal?: BudgetRenewalType;
286 expiresAt?: string;
287 scopes: Scope[];
288 returnTo?: string;
289 isolated?: boolean;
290 metadata?: AppMetadata;
291 unlockPassword?: string; // required to create superuser apps
292 }
293
294 export interface CreateAppResponse {
295 id: number;
296 name: string;
297 pairingUri: string;
298 pairingPublicKey: string;
299 pairingSecretKey: string;
300 relayUrls: string[];
301 walletPubkey: string;
302 lud16: string;
303 returnTo: string;
304 }
305
306 export type UpdateAppRequest = {
307 name?: string;
308 maxAmountSat?: number;
309 maxAmountMsat?: number;
310 budgetRenewal?: string;
311 expiresAt?: string | undefined;
312 updateExpiresAt?: boolean;
313 scopes?: Scope[];
314 metadata?: AppMetadata;
315 isolated?: boolean;
316 };
317
318 export type Channel = {
319 localBalanceSat: number;
320 localBalanceMsat: number;
321 localSpendableBalanceSat: number;
322 localSpendableBalanceMsat: number;
323 remoteBalanceSat: number;
324 remoteBalanceMsat: number;
325 remotePubkey: string;
326 id: string;
327 fundingTxId: string;
328 fundingTxVout: number;
329 active: boolean;
330 public: boolean;
331 confirmations?: number;
332 confirmationsRequired?: number;
333 forwardingFeeBaseMsat: number;
334 forwardingFeeProportionalMillionths: number;
335 unspendablePunishmentReserveSat: number;
336 counterpartyUnspendablePunishmentReserveSat: number;
337 error?: string;
338 status: "online" | "opening" | "offline";
339 isOutbound: boolean;
340 };
341
342 export type UpdateChannelRequest = {
343 forwardingFeeBaseMsat: number;
344 };
345
346 export type Peer = {
347 nodeId: string;
348 address: string;
349 isPersisted: boolean;
350 isConnected: boolean;
351 };
352
353 export type NodeConnectionInfo = {
354 pubkey: string;
355 address: string;
356 port: number;
357 };
358
359 export type ConnectPeerRequest = {
360 pubkey: string;
361 address: string;
362 port: number;
363 };
364
365 export type SignMessageRequest = {
366 message: string;
367 };
368
369 export type SignMessageResponse = {
370 message: string;
371 signature: string;
372 };
373
374 export type PayInvoiceResponse = Transaction;
375
376 export type CreateOfferRequest = {
377 description: string;
378 };
379
380 export type CreateInvoiceRequest = {
381 amountSat?: number;
382 amountMsat?: number;
383 description: string;
384 toAppId?: number;
385 };
386
387 export type PayInvoiceRequest = {
388 amountSat?: number;
389 amountMsat?: number;
390 metadata?: Record<string, unknown>;
391 fromAppId?: number;
392 };
393
394 export type OpenChannelRequest = {
395 pubkey: string;
396 amountSats: number;
397 public: boolean;
398 };
399
400 export type OpenChannelResponse = {
401 fundingTxId: string;
402 };
403
404 // eslint-disable-next-line @typescript-eslint/no-empty-object-type
405 export type CloseChannelResponse = {};
406
407 export type PendingBalancesDetails = {
408 channelId: string;
409 nodeId: string;
410 amountSat: number;
411 fundingTxId: string;
412 fundingTxVout: number;
413 };
414
415 export type OnchainBalanceResponse = {
416 spendableSat: number;
417 totalSat: number;
418 reservedSat: number;
419 pendingBalancesFromChannelClosuresSat: number;
420 pendingBalancesDetails: PendingBalancesDetails[];
421 pendingSweepBalancesDetails: PendingBalancesDetails[];
422 };
423
424 // from https://mempool.space/docs/api/rest#get-address-utxo
425 export type MempoolUtxo = {
426 txid: string;
427 vout: number;
428 status: {
429 confirmed: boolean;
430 block_height?: number;
431 block_hash?: string;
432 block_time?: number;
433 };
434 value: number;
435 };
436
437 // from https://mempool.space/docs/api/rest#get-node-stats
438 export type MempoolNode = {
439 alias: string;
440 public_key: string;
441 color: string;
442 active_channel_count: number;
443 sockets: string;
444 };
445
446 // from https://mempool.space/docs/api/rest#get-transaction
447 export type MempoolTransaction = {
448 txid: string;
449 //version: 1,
450 //locktime: 0,
451 // vin: [],
452 //vout: [],
453 size: number;
454 weight: number;
455 fee: number;
456 status:
457 | {
458 confirmed: true;
459 block_height: number;
460 block_hash: string;
461 block_time: number;
462 }
463 | { confirmed: false };
464 };
465
466 export type LongUnconfirmedZeroConfChannel = { id: string; message: string };
467
468 export type SetupNodeInfo = Partial<{
469 backendType: BackendType;
470
471 mnemonic?: string;
472 nextBackupReminder?: string;
473
474 lndAddress?: string;
475 lndCertFile?: string;
476 lndMacaroonFile?: string;
477
478 phoenixdAddress?: string;
479 phoenixdAuthorization?: string;
480
481 clnAddress?: string;
482 clnLightningDir?: string;
483 clnAddressHold?: string;
484 }>;
485
486 export type LSPType = "LSPS1" | "LSPS2";
487
488 export type LSPChannelOfferPaymentMethod =
489 | "card"
490 | "wallet"
491 | "prepaid"
492 | "included";
493
494 export type LSPChannelOffer = {
495 lspName: string;
496 lspDescription: string;
497 lspContactUrl: string;
498 lspBalanceSat: number;
499 feeTotalSat: number;
500 feeTotalUsd: number;
501 currentPaymentMethod: LSPChannelOfferPaymentMethod;
502 terms: string;
503 };
504
505 export type RecommendedChannelPeer = {
506 network: Network;
507 image: string;
508 name: string;
509 minimumChannelSizeSat: number;
510 maximumChannelSizeSat: number;
511 note: string;
512 publicChannelsAllowed: boolean;
513 description: string;
514 } & (
515 | {
516 paymentMethod: "onchain";
517 pubkey: string;
518 host: string;
519 }
520 | ({
521 paymentMethod: "lightning";
522 identifier: string;
523 contactUrl: string;
524 terms?: string;
525 pubkey?: string;
526 maximumChannelExpiryBlocks?: number;
527 feeTotalSat1m?: number;
528 feeTotalSat2m?: number;
529 feeTotalSat3m?: number;
530 } & (
531 | { type: "LSPS1" }
532 | { type: "LSPS2"; nodeAddress: string } // nodeid@ip:port
533 ))
534 );
535
536 export type AlbyInfo = {
537 hub: {
538 latestVersion: string;
539 latestReleaseNotes: string;
540 };
541 };
542
543 export type BitcoinRate = {
544 code: string;
545 symbol: string;
546 rate: string;
547 rate_float: number;
548 rate_cents: number;
549 };
550
551 export type Currency = {
552 iso_code: string;
553 symbol: string;
554 name: string;
555 priority: number;
556 };
557
558 // TODO: use camel case (needs mapping in the Alby OAuth Service - see how AlbyInfo is done above)
559 export type AlbyMe = {
560 identifier: string;
561 nostr_pubkey: string;
562 lightning_address: string;
563 email: string;
564 name: string;
565 avatar: string;
566 keysend_pubkey: string;
567 shared_node: boolean;
568 hub: {
569 name?: string;
570 config?: {
571 region?: string;
572 };
573 };
574 subscription: {
575 plan_code: string;
576 };
577 };
578
579 export type LSPOrderRequest = {
580 amountSat?: number;
581 lspType: LSPType;
582 lspIdentifier: string;
583 public: boolean;
584 };
585
586 export type RedeemOnchainFundsRequest = {
587 toAddress: string;
588 amountSat?: number;
589 feeRate?: number;
590 sendAll?: boolean;
591 };
592
593 export type AutoSwapRequest = {
594 balanceThresholdSat?: number;
595 swapAmountSat?: number;
596 destination: string;
597 destinationType?: string;
598 unlockPassword?: string;
599 };
600
601 export type InitiateSwapRequest = {
602 swapAmountSat?: number;
603 destination?: string;
604 };
605
606 export type LSPOrderResponse = {
607 invoice?: string;
608 feeSat: number;
609 invoiceAmountSat: number;
610 incomingLiquiditySat: number;
611 outgoingLiquiditySat: number;
612 };
613
614 export type AutoChannelRequest = {
615 isPublic: boolean;
616 };
617 export type AutoChannelResponse = {
618 invoice?: string;
619 feeSat?: number;
620 channelSizeSat: number;
621 };
622
623 export type RedeemOnchainFundsResponse = {
624 txId: string;
625 };
626
627 export type LightningBalanceResponse = {
628 totalSpendableSat: number;
629 totalSpendableMsat: number;
630 totalReceivableSat: number;
631 totalReceivableMsat: number;
632 nextMaxSpendableSat: number;
633 nextMaxSpendableMsat: number;
634 nextMaxReceivableSat: number;
635 nextMaxReceivableMsat: number;
636 nextMaxSpendableMPPSat: number;
637 nextMaxSpendableMPPMsat: number;
638 nextMaxReceivableMPPSat: number;
639 nextMaxReceivableMPPMsat: number;
640 };
641
642 export type BalancesResponse = {
643 onchain: OnchainBalanceResponse;
644 lightning: LightningBalanceResponse;
645 };
646
647 export type Transaction = {
648 id: number;
649 type: "incoming" | "outgoing";
650 state: "settled" | "pending" | "failed";
651 appId: number | undefined;
652 invoice: string;
653 description: string;
654 descriptionHash: string;
655 preimage: string | undefined;
656 paymentHash: string;
657 amountSat: number;
658 amountMsat: number;
659 feesPaidSat: number;
660 feesPaidMsat: number;
661 updatedAt: string;
662 createdAt: string;
663 settledAt: string | undefined;
664 metadata?: TransactionMetadata;
665 boostagram?: Boostagram;
666 failureReason: string;
667 };
668
669 export type TransactionMetadata = {
670 comment?: string; // LUD-12
671 payer_data?: {
672 email?: string;
673 name?: string;
674 pubkey?: string;
675 }; // LUD-18
676 recipient_data?: {
677 identifier?: string;
678 }; // LUD-18
679 nostr?: {
680 pubkey: string;
681 tags: string[][];
682 }; // NIP-57
683 offer?: {
684 id: string;
685 payer_note: string;
686 }; // BOLT-12
687 swap_id?: string;
688 user_labels?: Record<string, string>;
689 } & Record<string, unknown>;
690
691 export type Boostagram = {
692 appName: string;
693 name: string;
694 podcast: string;
695 url: string;
696 episode?: string;
697 feedId?: string;
698 itemId?: string;
699 ts?: number;
700 message?: string;
701 senderId: string;
702 senderName: string;
703 time: string;
704 action: "boost";
705 valueSatTotal: number;
706 valueMsatTotal: number;
707 };
708
709 export type OnchainTransaction = {
710 amountSat: number;
711 amountMsat: number;
712 createdAt: number;
713 type: "incoming" | "outgoing";
714 state: "confirmed" | "unconfirmed";
715 numConfirmations: number;
716 txId: string;
717 };
718
719 export type ListAppsResponse = {
720 apps: App[];
721 totalCount: number;
722 totalBalanceSat?: number;
723 totalBalanceMsat?: number;
724 };
725
726 export type ListTransactionsResponse = {
727 transactions: Transaction[];
728 totalCount: number;
729 };
730
731 export type NewChannelOrderStatus = "pay" | "paid" | "success" | "opening";
732
733 type NewChannelOrderCommon = {
734 amountSat: string;
735 isPublic: boolean;
736 status: NewChannelOrderStatus;
737 fundingTxId?: string;
738 prevChannelIds: string[];
739 };
740
741 export type OnchainOrder = {
742 paymentMethod: "onchain";
743 pubkey: string;
744 host: string;
745 } & NewChannelOrderCommon;
746
747 export type LightningOrder = {
748 paymentMethod: "lightning";
749 lspType: LSPType;
750 lspIdentifier: string;
751 } & NewChannelOrderCommon;
752
753 export type NewChannelOrder = OnchainOrder | LightningOrder;
754
755 export type AuthTokenResponse = {
756 token: string;
757 };
758
759 export type GetForwardsResponse = {
760 outboundAmountForwardedSat: number;
761 outboundAmountForwardedMsat: number;
762 totalFeeEarnedSat: number;
763 totalFeeEarnedMsat: number;
764 numForwards: number;
765 };
766