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 nip07AuthEnabled: boolean;
179 currency: string;
180 nodeAlias: string;
181 mempoolUrl: string;
182 bitcoinDisplayFormat: BitcoinDisplayFormat;
183 chainDataSourceType?: string;
184 chainDataSourceAddress?: string;
185 jitChannelsLiquiditySource?: string;
186 jitChannelsMinPaymentSizeMsat?: number;
187 jitChannelsMaxPaymentSizeMsat?: number;
188 jitChannelsEnabled: boolean;
189 hideUpdateBanner: boolean;
190 supportsBolt12: boolean;
191 nodeMigrationFileCreated: boolean;
192 }
193
194 export type BitcoinDisplayFormat = "sats" | "bip177";
195
196 export type HealthAlarmKind =
197 | "alby_service"
198 | "node_not_ready"
199 | "channels_offline"
200 | "nostr_relay_offline"
201 | "vss_no_subscription";
202
203 export type HealthAlarm = {
204 kind: HealthAlarmKind;
205 rawDetails?: unknown;
206 };
207 export type AlbyInfoIncident = {
208 name: string;
209 started: string;
210 status: string;
211 impact: string;
212 url: string;
213 };
214
215 export type HealthResponse = {
216 alarms: HealthAlarm[];
217 };
218
219 export type Network = "bitcoin" | "testnet" | "signet";
220
221 export type AppMetadata = {
222 app_store_app_id?: string;
223 lud16?: string;
224 } & Record<string, unknown>;
225
226 export type AutoSwapConfig = {
227 type: "out";
228 enabled: boolean;
229 balanceThresholdSat: number;
230 swapAmountSat: number;
231 destination: string;
232 };
233
234 export type SwapInfo = {
235 albyServiceFee: number;
236 boltzServiceFee: number;
237 boltzNetworkFeeSat: number;
238 minAmountSat: number;
239 maxAmountSat: number;
240 };
241
242 export type BaseSwap = {
243 id: string;
244 sendAmountSat: number;
245 lockupAddress: string;
246 paymentHash: string;
247 invoice: string;
248 autoSwap: boolean;
249 usedXpub: boolean;
250 boltzPubkey: string;
251 createdAt: string;
252 updatedAt: string;
253 lockupTxId?: string;
254 claimTxId?: string;
255 receiveAmountSat?: number;
256 };
257
258 export type SwapIn = BaseSwap & {
259 type: "in";
260 state: "PENDING" | "SUCCESS" | "FAILED" | "REFUNDED";
261 refundAddress?: string;
262 };
263
264 export type SwapOut = BaseSwap & {
265 type: "out";
266 state: "PENDING" | "SUCCESS" | "FAILED";
267 destinationAddress: string;
268 };
269
270 export type Swap = SwapIn | SwapOut;
271
272 export type SwapResponse = {
273 swapId: string;
274 paymentHash: string;
275 };
276
277 export interface MnemonicResponse {
278 mnemonic: string;
279 }
280
281 export interface CreateAppRequest {
282 name: string;
283 pubkey?: string;
284 maxAmountSat?: number;
285 maxAmountMsat?: number;
286 budgetRenewal?: BudgetRenewalType;
287 expiresAt?: string;
288 scopes: Scope[];
289 returnTo?: string;
290 isolated?: boolean;
291 metadata?: AppMetadata;
292 unlockPassword?: string; // required to create superuser apps
293 }
294
295 export interface CreateAppResponse {
296 id: number;
297 name: string;
298 pairingUri: string;
299 pairingPublicKey: string;
300 pairingSecretKey: string;
301 relayUrls: string[];
302 walletPubkey: string;
303 lud16: string;
304 returnTo: string;
305 }
306
307 export type UpdateAppRequest = {
308 name?: string;
309 maxAmountSat?: number;
310 maxAmountMsat?: number;
311 budgetRenewal?: string;
312 expiresAt?: string | undefined;
313 updateExpiresAt?: boolean;
314 scopes?: Scope[];
315 metadata?: AppMetadata;
316 isolated?: boolean;
317 };
318
319 export type Channel = {
320 localBalanceSat: number;
321 localBalanceMsat: number;
322 localSpendableBalanceSat: number;
323 localSpendableBalanceMsat: number;
324 remoteBalanceSat: number;
325 remoteBalanceMsat: number;
326 remotePubkey: string;
327 id: string;
328 fundingTxId: string;
329 fundingTxVout: number;
330 active: boolean;
331 public: boolean;
332 confirmations?: number;
333 confirmationsRequired?: number;
334 forwardingFeeBaseMsat: number;
335 forwardingFeeProportionalMillionths: number;
336 unspendablePunishmentReserveSat: number;
337 counterpartyUnspendablePunishmentReserveSat: number;
338 error?: string;
339 status: "online" | "opening" | "offline";
340 isOutbound: boolean;
341 };
342
343 export type UpdateChannelRequest = {
344 forwardingFeeBaseMsat: number;
345 };
346
347 export type Peer = {
348 nodeId: string;
349 address: string;
350 isPersisted: boolean;
351 isConnected: boolean;
352 };
353
354 export type NodeConnectionInfo = {
355 pubkey: string;
356 address: string;
357 port: number;
358 };
359
360 export type ConnectPeerRequest = {
361 pubkey: string;
362 address: string;
363 port: number;
364 };
365
366 export type SignMessageRequest = {
367 message: string;
368 };
369
370 export type SignMessageResponse = {
371 message: string;
372 signature: string;
373 };
374
375 export type PayInvoiceResponse = Transaction;
376
377 export type CreateOfferRequest = {
378 description: string;
379 };
380
381 export type CreateInvoiceRequest = {
382 amountSat?: number;
383 amountMsat?: number;
384 description: string;
385 toAppId?: number;
386 };
387
388 export type PayInvoiceRequest = {
389 amountSat?: number;
390 amountMsat?: number;
391 metadata?: Record<string, unknown>;
392 fromAppId?: number;
393 };
394
395 export type OpenChannelRequest = {
396 pubkey: string;
397 amountSats: number;
398 public: boolean;
399 };
400
401 export type OpenChannelResponse = {
402 fundingTxId: string;
403 };
404
405 // eslint-disable-next-line @typescript-eslint/no-empty-object-type
406 export type CloseChannelResponse = {};
407
408 export type PendingBalancesDetails = {
409 channelId: string;
410 nodeId: string;
411 amountSat: number;
412 fundingTxId: string;
413 fundingTxVout: number;
414 };
415
416 export type OnchainBalanceResponse = {
417 spendableSat: number;
418 totalSat: number;
419 reservedSat: number;
420 pendingBalancesFromChannelClosuresSat: number;
421 pendingBalancesDetails: PendingBalancesDetails[];
422 pendingSweepBalancesDetails: PendingBalancesDetails[];
423 };
424
425 // from https://mempool.space/docs/api/rest#get-address-utxo
426 export type MempoolUtxo = {
427 txid: string;
428 vout: number;
429 status: {
430 confirmed: boolean;
431 block_height?: number;
432 block_hash?: string;
433 block_time?: number;
434 };
435 value: number;
436 };
437
438 // from https://mempool.space/docs/api/rest#get-node-stats
439 export type MempoolNode = {
440 alias: string;
441 public_key: string;
442 color: string;
443 active_channel_count: number;
444 sockets: string;
445 };
446
447 // from https://mempool.space/docs/api/rest#get-transaction
448 export type MempoolTransaction = {
449 txid: string;
450 //version: 1,
451 //locktime: 0,
452 // vin: [],
453 //vout: [],
454 size: number;
455 weight: number;
456 fee: number;
457 status:
458 | {
459 confirmed: true;
460 block_height: number;
461 block_hash: string;
462 block_time: number;
463 }
464 | { confirmed: false };
465 };
466
467 export type LongUnconfirmedZeroConfChannel = { id: string; message: string };
468
469 export type SetupNodeInfo = Partial<{
470 backendType: BackendType;
471
472 mnemonic?: string;
473 nextBackupReminder?: string;
474
475 lndAddress?: string;
476 lndCertFile?: string;
477 lndMacaroonFile?: string;
478
479 phoenixdAddress?: string;
480 phoenixdAuthorization?: string;
481
482 clnAddress?: string;
483 clnLightningDir?: string;
484 clnAddressHold?: string;
485 }>;
486
487 export type LSPType = "LSPS1" | "LSPS2";
488
489 export type LSPChannelOfferPaymentMethod =
490 | "card"
491 | "wallet"
492 | "prepaid"
493 | "included";
494
495 export type LSPChannelOffer = {
496 lspName: string;
497 lspDescription: string;
498 lspContactUrl: string;
499 lspBalanceSat: number;
500 feeTotalSat: number;
501 feeTotalUsd: number;
502 currentPaymentMethod: LSPChannelOfferPaymentMethod;
503 terms: string;
504 };
505
506 export type RecommendedChannelPeer = {
507 network: Network;
508 image: string;
509 name: string;
510 minimumChannelSizeSat: number;
511 maximumChannelSizeSat: number;
512 note: string;
513 publicChannelsAllowed: boolean;
514 description: string;
515 } & (
516 | {
517 paymentMethod: "onchain";
518 pubkey: string;
519 host: string;
520 }
521 | ({
522 paymentMethod: "lightning";
523 identifier: string;
524 contactUrl: string;
525 terms?: string;
526 pubkey?: string;
527 maximumChannelExpiryBlocks?: number;
528 feeTotalSat1m?: number;
529 feeTotalSat2m?: number;
530 feeTotalSat3m?: number;
531 } & (
532 | { type: "LSPS1" }
533 | { type: "LSPS2"; nodeAddress: string } // nodeid@ip:port
534 ))
535 );
536
537 export type AlbyInfo = {
538 hub: {
539 latestVersion: string;
540 latestReleaseNotes: string;
541 };
542 };
543
544 export type BitcoinRate = {
545 code: string;
546 symbol: string;
547 rate: string;
548 rate_float: number;
549 rate_cents: number;
550 };
551
552 export type Currency = {
553 iso_code: string;
554 symbol: string;
555 name: string;
556 priority: number;
557 };
558
559 // TODO: use camel case (needs mapping in the Alby OAuth Service - see how AlbyInfo is done above)
560 export type AlbyMe = {
561 identifier: string;
562 nostr_pubkey: string;
563 lightning_address: string;
564 email: string;
565 name: string;
566 avatar: string;
567 keysend_pubkey: string;
568 shared_node: boolean;
569 hub: {
570 name?: string;
571 config?: {
572 region?: string;
573 };
574 };
575 subscription: {
576 plan_code: string;
577 };
578 };
579
580 export type LSPOrderRequest = {
581 amountSat?: number;
582 lspType: LSPType;
583 lspIdentifier: string;
584 public: boolean;
585 };
586
587 export type RedeemOnchainFundsRequest = {
588 toAddress: string;
589 amountSat?: number;
590 feeRate?: number;
591 sendAll?: boolean;
592 };
593
594 export type AutoSwapRequest = {
595 balanceThresholdSat?: number;
596 swapAmountSat?: number;
597 destination: string;
598 destinationType?: string;
599 unlockPassword?: string;
600 };
601
602 export type InitiateSwapRequest = {
603 swapAmountSat?: number;
604 destination?: string;
605 };
606
607 export type LSPOrderResponse = {
608 invoice?: string;
609 feeSat: number;
610 invoiceAmountSat: number;
611 incomingLiquiditySat: number;
612 outgoingLiquiditySat: number;
613 };
614
615 export type AutoChannelRequest = {
616 isPublic: boolean;
617 };
618 export type AutoChannelResponse = {
619 invoice?: string;
620 feeSat?: number;
621 channelSizeSat: number;
622 };
623
624 export type RedeemOnchainFundsResponse = {
625 txId: string;
626 };
627
628 export type LightningBalanceResponse = {
629 totalSpendableSat: number;
630 totalSpendableMsat: number;
631 totalReceivableSat: number;
632 totalReceivableMsat: number;
633 nextMaxSpendableSat: number;
634 nextMaxSpendableMsat: number;
635 nextMaxReceivableSat: number;
636 nextMaxReceivableMsat: number;
637 nextMaxSpendableMPPSat: number;
638 nextMaxSpendableMPPMsat: number;
639 nextMaxReceivableMPPSat: number;
640 nextMaxReceivableMPPMsat: number;
641 };
642
643 export type BalancesResponse = {
644 onchain: OnchainBalanceResponse;
645 lightning: LightningBalanceResponse;
646 };
647
648 export type Transaction = {
649 id: number;
650 type: "incoming" | "outgoing";
651 state: "settled" | "pending" | "failed";
652 appId: number | undefined;
653 invoice: string;
654 description: string;
655 descriptionHash: string;
656 preimage: string | undefined;
657 paymentHash: string;
658 amountSat: number;
659 amountMsat: number;
660 feesPaidSat: number;
661 feesPaidMsat: number;
662 updatedAt: string;
663 createdAt: string;
664 settledAt: string | undefined;
665 metadata?: TransactionMetadata;
666 boostagram?: Boostagram;
667 failureReason: string;
668 };
669
670 export type TransactionMetadata = {
671 comment?: string; // LUD-12
672 payer_data?: {
673 email?: string;
674 name?: string;
675 pubkey?: string;
676 }; // LUD-18
677 recipient_data?: {
678 identifier?: string;
679 }; // LUD-18
680 nostr?: {
681 pubkey: string;
682 tags: string[][];
683 }; // NIP-57
684 offer?: {
685 id: string;
686 payer_note: string;
687 }; // BOLT-12
688 swap_id?: string;
689 user_labels?: Record<string, string>;
690 } & Record<string, unknown>;
691
692 export type Boostagram = {
693 appName: string;
694 name: string;
695 podcast: string;
696 url: string;
697 episode?: string;
698 feedId?: string;
699 itemId?: string;
700 ts?: number;
701 message?: string;
702 senderId: string;
703 senderName: string;
704 time: string;
705 action: "boost";
706 valueSatTotal: number;
707 valueMsatTotal: number;
708 };
709
710 export type OnchainTransaction = {
711 amountSat: number;
712 amountMsat: number;
713 createdAt: number;
714 type: "incoming" | "outgoing";
715 state: "confirmed" | "unconfirmed";
716 numConfirmations: number;
717 txId: string;
718 };
719
720 export type ListAppsResponse = {
721 apps: App[];
722 totalCount: number;
723 totalBalanceSat?: number;
724 totalBalanceMsat?: number;
725 };
726
727 export type ListTransactionsResponse = {
728 transactions: Transaction[];
729 totalCount: number;
730 };
731
732 export type NewChannelOrderStatus = "pay" | "paid" | "success" | "opening";
733
734 type NewChannelOrderCommon = {
735 amountSat: string;
736 isPublic: boolean;
737 status: NewChannelOrderStatus;
738 fundingTxId?: string;
739 prevChannelIds: string[];
740 };
741
742 export type OnchainOrder = {
743 paymentMethod: "onchain";
744 pubkey: string;
745 host: string;
746 } & NewChannelOrderCommon;
747
748 export type LightningOrder = {
749 paymentMethod: "lightning";
750 lspType: LSPType;
751 lspIdentifier: string;
752 } & NewChannelOrderCommon;
753
754 export type NewChannelOrder = OnchainOrder | LightningOrder;
755
756 export type AuthTokenResponse = {
757 token: string;
758 };
759
760 export type GetForwardsResponse = {
761 outboundAmountForwardedSat: number;
762 outboundAmountForwardedMsat: number;
763 totalFeeEarnedSat: number;
764 totalFeeEarnedMsat: number;
765 numForwards: number;
766 };
767