applesauce-wallet-connect 0.0.0-next-20250808173123

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 (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/actions/index.d.ts +1 -0
  4. package/dist/actions/index.js +1 -0
  5. package/dist/actions/tokens.d.ts +17 -0
  6. package/dist/actions/tokens.js +110 -0
  7. package/dist/actions/wallet.d.ts +13 -0
  8. package/dist/actions/wallet.js +64 -0
  9. package/dist/actions/zap-info.d.ts +22 -0
  10. package/dist/actions/zap-info.js +83 -0
  11. package/dist/actions/zaps.d.ts +8 -0
  12. package/dist/actions/zaps.js +30 -0
  13. package/dist/blueprints/history.d.ts +4 -0
  14. package/dist/blueprints/history.js +11 -0
  15. package/dist/blueprints/index.d.ts +4 -0
  16. package/dist/blueprints/index.js +4 -0
  17. package/dist/blueprints/info.d.ts +4 -0
  18. package/dist/blueprints/info.js +8 -0
  19. package/dist/blueprints/notification.d.ts +15 -0
  20. package/dist/blueprints/notification.js +21 -0
  21. package/dist/blueprints/request.d.ts +9 -0
  22. package/dist/blueprints/request.js +12 -0
  23. package/dist/blueprints/response.d.ts +5 -0
  24. package/dist/blueprints/response.js +10 -0
  25. package/dist/blueprints/support.d.ts +4 -0
  26. package/dist/blueprints/support.js +8 -0
  27. package/dist/blueprints/tokens.d.ts +7 -0
  28. package/dist/blueprints/tokens.js +11 -0
  29. package/dist/blueprints/wallet.d.ts +5 -0
  30. package/dist/blueprints/wallet.js +11 -0
  31. package/dist/blueprints/zaps.d.ts +8 -0
  32. package/dist/blueprints/zaps.js +12 -0
  33. package/dist/helpers/animated-qr.d.ts +30 -0
  34. package/dist/helpers/animated-qr.js +71 -0
  35. package/dist/helpers/connect-uri.d.ts +12 -0
  36. package/dist/helpers/connect-uri.js +23 -0
  37. package/dist/helpers/encryption.d.ts +2 -0
  38. package/dist/helpers/encryption.js +1 -0
  39. package/dist/helpers/error.d.ts +55 -0
  40. package/dist/helpers/error.js +81 -0
  41. package/dist/helpers/history.d.ts +26 -0
  42. package/dist/helpers/history.js +47 -0
  43. package/dist/helpers/index.d.ts +7 -0
  44. package/dist/helpers/index.js +7 -0
  45. package/dist/helpers/info.d.ts +34 -0
  46. package/dist/helpers/info.js +97 -0
  47. package/dist/helpers/methods.d.ts +1 -0
  48. package/dist/helpers/methods.js +1 -0
  49. package/dist/helpers/notification.d.ts +28 -0
  50. package/dist/helpers/notification.js +28 -0
  51. package/dist/helpers/nutzap.d.ts +27 -0
  52. package/dist/helpers/nutzap.js +66 -0
  53. package/dist/helpers/request.d.ts +131 -0
  54. package/dist/helpers/request.js +51 -0
  55. package/dist/helpers/response.d.ts +138 -0
  56. package/dist/helpers/response.js +35 -0
  57. package/dist/helpers/support.d.ts +34 -0
  58. package/dist/helpers/support.js +97 -0
  59. package/dist/helpers/tokens.d.ts +58 -0
  60. package/dist/helpers/tokens.js +162 -0
  61. package/dist/helpers/wallet.d.ts +15 -0
  62. package/dist/helpers/wallet.js +41 -0
  63. package/dist/helpers/zap-info.d.ts +19 -0
  64. package/dist/helpers/zap-info.js +42 -0
  65. package/dist/index.d.ts +5 -0
  66. package/dist/index.js +5 -0
  67. package/dist/interface.d.ts +6 -0
  68. package/dist/interface.js +1 -0
  69. package/dist/models/history.d.ts +6 -0
  70. package/dist/models/history.js +21 -0
  71. package/dist/models/index.d.ts +4 -0
  72. package/dist/models/index.js +4 -0
  73. package/dist/models/nutzap.d.ts +6 -0
  74. package/dist/models/nutzap.js +16 -0
  75. package/dist/models/tokens.d.ts +6 -0
  76. package/dist/models/tokens.js +58 -0
  77. package/dist/models/wallet.d.ts +13 -0
  78. package/dist/models/wallet.js +18 -0
  79. package/dist/operations/history.d.ts +7 -0
  80. package/dist/operations/history.js +34 -0
  81. package/dist/operations/index.d.ts +5 -0
  82. package/dist/operations/index.js +5 -0
  83. package/dist/operations/nutzap.d.ts +14 -0
  84. package/dist/operations/nutzap.js +33 -0
  85. package/dist/operations/tokens.d.ts +4 -0
  86. package/dist/operations/tokens.js +24 -0
  87. package/dist/operations/wallet.d.ts +8 -0
  88. package/dist/operations/wallet.js +30 -0
  89. package/dist/operations/zap-info.d.ts +10 -0
  90. package/dist/operations/zap-info.js +17 -0
  91. package/dist/types.d.ts +6 -0
  92. package/dist/types.js +1 -0
  93. package/dist/wallet-connect.d.ts +111 -0
  94. package/dist/wallet-connect.js +271 -0
  95. package/dist/wallet-service.d.ts +111 -0
  96. package/dist/wallet-service.js +270 -0
  97. package/package.json +83 -0
@@ -0,0 +1,11 @@
1
+ import { blueprint } from "applesauce-factory";
2
+ import { WALLET_TOKEN_KIND } from "../helpers/tokens.js";
3
+ import { setToken } from "../operations/tokens.js";
4
+ /**
5
+ * A blueprint for a wallet token event, takes a cashu token and previous deleted token event ids
6
+ * @param token the cashu token to store
7
+ * @param [del=[]] an array of previous token event ids that are deleted
8
+ */
9
+ export function WalletTokenBlueprint(token, del = []) {
10
+ return blueprint(WALLET_TOKEN_KIND, setToken(token, del));
11
+ }
@@ -0,0 +1,5 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ /** A blueprint to create a new 17375 wallet */
3
+ export declare function WalletBlueprint(mints: string[], privateKey?: Uint8Array): import("applesauce-factory").EventBlueprint;
4
+ /** A blueprint that creates a new 375 wallet backup event */
5
+ export declare function WalletBackupBlueprint(wallet: NostrEvent): import("applesauce-factory").EventBlueprint;
@@ -0,0 +1,11 @@
1
+ import { blueprint } from "applesauce-factory";
2
+ import { WALLET_BACKUP_KIND, WALLET_KIND } from "../helpers/wallet.js";
3
+ import { setBackupContent, setMints, setPrivateKey } from "../operations/wallet.js";
4
+ /** A blueprint to create a new 17375 wallet */
5
+ export function WalletBlueprint(mints, privateKey) {
6
+ return blueprint(WALLET_KIND, setMints(mints), privateKey ? setPrivateKey(privateKey) : undefined);
7
+ }
8
+ /** A blueprint that creates a new 375 wallet backup event */
9
+ export function WalletBackupBlueprint(wallet) {
10
+ return blueprint(WALLET_BACKUP_KIND, setBackupContent(wallet));
11
+ }
@@ -0,0 +1,8 @@
1
+ import { Token } from "@cashu/cashu-ts";
2
+ import { EventBlueprint } from "applesauce-factory";
3
+ import { NostrEvent } from "nostr-tools";
4
+ import { ProfilePointer } from "nostr-tools/nip19";
5
+ /** A blueprint to create a NIP-61 nutzap event for an event */
6
+ export declare function NutzapBlueprint(event: NostrEvent, token: Token, comment?: string): EventBlueprint;
7
+ /** A blueprint to create a NIP-61 nutzap event for a user instead of an event */
8
+ export declare function ProfileNutzapBlueprint(user: string | ProfilePointer, token: Token, comment?: string): EventBlueprint;
@@ -0,0 +1,12 @@
1
+ import { blueprint } from "applesauce-factory";
2
+ import { skip } from "applesauce-factory/helpers";
3
+ import { NUTZAP_KIND } from "../helpers/nutzap.js";
4
+ import { setComment, setEvent, setMint, setProofs, setRecipient } from "../operations/nutzap.js";
5
+ /** A blueprint to create a NIP-61 nutzap event for an event */
6
+ export function NutzapBlueprint(event, token, comment) {
7
+ return blueprint(NUTZAP_KIND, setProofs(token.proofs), setMint(token.mint), setEvent(event), setRecipient(event.pubkey), comment ? setComment(comment) : skip());
8
+ }
9
+ /** A blueprint to create a NIP-61 nutzap event for a user instead of an event */
10
+ export function ProfileNutzapBlueprint(user, token, comment) {
11
+ return blueprint(NUTZAP_KIND, setProofs(token.proofs), setMint(token.mint), setRecipient(user), comment ? setComment(comment) : skip());
12
+ }
@@ -0,0 +1,30 @@
1
+ import { Token } from "@cashu/cashu-ts";
2
+ import { Observable } from "rxjs";
3
+ /** Preset speeds for the animated qr code */
4
+ export declare const ANIMATED_QR_INTERVAL: {
5
+ SLOW: number;
6
+ MEDIUM: number;
7
+ FAST: number;
8
+ };
9
+ /** Presets for fragment length for animated qr code */
10
+ export declare const ANIMATED_QR_FRAGMENTS: {
11
+ SHORT: number;
12
+ MEDIUM: number;
13
+ LONG: number;
14
+ };
15
+ export type SendAnimatedOptions = {
16
+ /**
17
+ * The interval between the parts ( 150 - 500 )
18
+ * @default 150
19
+ */
20
+ interval?: number;
21
+ /**
22
+ * max fragment length ( 50 - 200 )
23
+ * @default 100
24
+ */
25
+ fragmentLength?: number;
26
+ };
27
+ /** Creates an observable that iterates through a multi-part animated qr code */
28
+ export declare function sendAnimated(token: Token | string, options?: SendAnimatedOptions): Observable<string>;
29
+ /** Creates an observable that completes with decoded token */
30
+ export declare function receiveAnimated(input: Observable<string>): Observable<string | number>;
@@ -0,0 +1,71 @@
1
+ import { getEncodedTokenV4 } from "@cashu/cashu-ts";
2
+ import { UR, URDecoder, UREncoder } from "@gandlaf21/bc-ur/dist/lib/es6/index.js";
3
+ import { defer, filter, interval, map, Observable, shareReplay } from "rxjs";
4
+ /** Preset speeds for the animated qr code */
5
+ export const ANIMATED_QR_INTERVAL = {
6
+ SLOW: 500,
7
+ MEDIUM: 250,
8
+ FAST: 150,
9
+ };
10
+ /** Presets for fragment length for animated qr code */
11
+ export const ANIMATED_QR_FRAGMENTS = {
12
+ SHORT: 50,
13
+ MEDIUM: 100,
14
+ LONG: 150,
15
+ };
16
+ /** Creates an observable that iterates through a multi-part animated qr code */
17
+ export function sendAnimated(token, options) {
18
+ // start the stream as soon as there is subscriber
19
+ return defer(() => {
20
+ let str = typeof token === "string" ? token : getEncodedTokenV4(token);
21
+ let utf8 = new TextEncoder();
22
+ let buffer = utf8.encode(str);
23
+ let ur = UR.from(buffer);
24
+ let encoder = new UREncoder(ur, options?.fragmentLength ?? 100, 0);
25
+ return interval(options?.interval ?? ANIMATED_QR_INTERVAL.FAST).pipe(map(() => encoder.nextPart()));
26
+ });
27
+ }
28
+ /** An operator that decodes UR, emits progress percent and completes with final result or error */
29
+ function urDecoder() {
30
+ return (source) => new Observable((observer) => {
31
+ const decoder = new URDecoder();
32
+ return source.subscribe((part) => {
33
+ decoder.receivePart(part);
34
+ if (decoder.isComplete() && decoder.isSuccess()) {
35
+ // emit progress
36
+ const progress = decoder.estimatedPercentComplete();
37
+ observer.next(progress);
38
+ // emit result
39
+ const ur = decoder.resultUR();
40
+ const decoded = ur.decodeCBOR();
41
+ const utf8 = new TextDecoder();
42
+ const tokenStr = utf8.decode(decoded);
43
+ observer.next(tokenStr);
44
+ // complete
45
+ observer.complete();
46
+ }
47
+ else if (decoder.isError()) {
48
+ // emit error
49
+ const reason = decoder.resultError();
50
+ observer.error(new Error(reason));
51
+ }
52
+ else {
53
+ // emit progress
54
+ const progress = decoder.estimatedPercentComplete();
55
+ observer.next(progress);
56
+ }
57
+ });
58
+ });
59
+ }
60
+ /** Creates an observable that completes with decoded token */
61
+ export function receiveAnimated(input) {
62
+ return input.pipe(
63
+ // convert to lower case
64
+ map((str) => str.toLowerCase()),
65
+ // filter out non UR parts
66
+ filter((str) => str.startsWith("ur:bytes")),
67
+ // decode UR and complete
68
+ urDecoder(),
69
+ // only run one decoder
70
+ shareReplay(1));
71
+ }
@@ -0,0 +1,12 @@
1
+ export interface WalletConnectURI {
2
+ service: string;
3
+ relays: string[];
4
+ secret: string;
5
+ }
6
+ /**
7
+ * Parses a nostr+walletconnect URI
8
+ * @throws {Error} if the connection string is invalid
9
+ */
10
+ export declare function parseWalletConnectURI(connectionString: string): WalletConnectURI;
11
+ /** Creates a nostr+walletconnect URI from a WalletConnectURI object */
12
+ export declare function createWalletConnectURI(parts: WalletConnectURI): string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Parses a nostr+walletconnect URI
3
+ * @throws {Error} if the connection string is invalid
4
+ */
5
+ export function parseWalletConnectURI(connectionString) {
6
+ const { host, pathname, searchParams, protocol } = new URL(connectionString);
7
+ if (protocol !== "nostr+walletconnect:")
8
+ throw new Error("invalid wallet connect uri protocol");
9
+ const service = pathname || host;
10
+ const relays = searchParams.getAll("relay");
11
+ const secret = searchParams.get("secret");
12
+ if (!service || relays.length === 0 || !secret)
13
+ throw new Error("invalid connection string");
14
+ return { service, relays, secret };
15
+ }
16
+ /** Creates a nostr+walletconnect URI from a WalletConnectURI object */
17
+ export function createWalletConnectURI(parts) {
18
+ const url = new URL(`nostr+walletconnect://${parts.service}`);
19
+ for (const relay of parts.relays)
20
+ url.searchParams.append("relay", relay);
21
+ url.searchParams.append("secret", parts.secret);
22
+ return url.toString();
23
+ }
@@ -0,0 +1,2 @@
1
+ /** Supported encryption methods */
2
+ export type WalletConnectEncryptionMethod = "nip44_v2" | "nip04";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ /** NIP-47 error codes as defined in the specification */
2
+ export type WalletErrorCode = "RATE_LIMITED" | "NOT_IMPLEMENTED" | "INSUFFICIENT_BALANCE" | "QUOTA_EXCEEDED" | "RESTRICTED" | "UNAUTHORIZED" | "INTERNAL" | "UNSUPPORTED_ENCRYPTION" | "PAYMENT_FAILED" | "NOT_FOUND" | "OTHER";
3
+ /** Base class for all NIP-47 wallet connect errors */
4
+ export declare abstract class WalletBaseError extends Error {
5
+ abstract readonly code: WalletErrorCode;
6
+ constructor(message: string);
7
+ }
8
+ /** The client is sending commands too fast. It should retry in a few seconds. */
9
+ export declare class RateLimitedError extends WalletBaseError {
10
+ readonly code: "RATE_LIMITED";
11
+ }
12
+ /** The command is not known or is intentionally not implemented. */
13
+ export declare class NotImplementedError extends WalletBaseError {
14
+ readonly code: "NOT_IMPLEMENTED";
15
+ }
16
+ /** The wallet does not have enough funds to cover a fee reserve or the payment amount. */
17
+ export declare class InsufficientBalanceError extends WalletBaseError {
18
+ readonly code: "INSUFFICIENT_BALANCE";
19
+ }
20
+ /** The wallet has exceeded its spending quota. */
21
+ export declare class QuotaExceededError extends WalletBaseError {
22
+ readonly code: "QUOTA_EXCEEDED";
23
+ }
24
+ /** This public key is not allowed to do this operation. */
25
+ export declare class RestrictedError extends WalletBaseError {
26
+ readonly code: "RESTRICTED";
27
+ }
28
+ /** This public key has no wallet connected. */
29
+ export declare class UnauthorizedError extends WalletBaseError {
30
+ readonly code: "UNAUTHORIZED";
31
+ }
32
+ /** An internal error. */
33
+ export declare class InternalError extends WalletBaseError {
34
+ readonly code: "INTERNAL";
35
+ }
36
+ /** The encryption type of the request is not supported by the wallet service. */
37
+ export declare class UnsupportedEncryptionError extends WalletBaseError {
38
+ readonly code: "UNSUPPORTED_ENCRYPTION";
39
+ }
40
+ /** The payment failed. This may be due to a timeout, exhausting all routes, insufficient capacity or similar. */
41
+ export declare class PaymentFailedError extends WalletBaseError {
42
+ readonly code: "PAYMENT_FAILED";
43
+ }
44
+ /** The invoice could not be found by the given parameters. */
45
+ export declare class NotFoundError extends WalletBaseError {
46
+ readonly code: "NOT_FOUND";
47
+ }
48
+ /** Other error. */
49
+ export declare class OtherError extends WalletBaseError {
50
+ readonly code: "OTHER";
51
+ }
52
+ /** Union type of all NIP-47 error classes */
53
+ export type WalletErrorClass = RateLimitedError | NotImplementedError | InsufficientBalanceError | QuotaExceededError | RestrictedError | UnauthorizedError | InternalError | UnsupportedEncryptionError | PaymentFailedError | NotFoundError | OtherError;
54
+ /** Factory function to create NWC error instances from error code and message */
55
+ export declare function createWalletError(code: WalletErrorCode, message: string): WalletErrorClass;
@@ -0,0 +1,81 @@
1
+ /** Base class for all NIP-47 wallet connect errors */
2
+ export class WalletBaseError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = this.constructor.name;
6
+ }
7
+ }
8
+ /** The client is sending commands too fast. It should retry in a few seconds. */
9
+ export class RateLimitedError extends WalletBaseError {
10
+ code = "RATE_LIMITED";
11
+ }
12
+ /** The command is not known or is intentionally not implemented. */
13
+ export class NotImplementedError extends WalletBaseError {
14
+ code = "NOT_IMPLEMENTED";
15
+ }
16
+ /** The wallet does not have enough funds to cover a fee reserve or the payment amount. */
17
+ export class InsufficientBalanceError extends WalletBaseError {
18
+ code = "INSUFFICIENT_BALANCE";
19
+ }
20
+ /** The wallet has exceeded its spending quota. */
21
+ export class QuotaExceededError extends WalletBaseError {
22
+ code = "QUOTA_EXCEEDED";
23
+ }
24
+ /** This public key is not allowed to do this operation. */
25
+ export class RestrictedError extends WalletBaseError {
26
+ code = "RESTRICTED";
27
+ }
28
+ /** This public key has no wallet connected. */
29
+ export class UnauthorizedError extends WalletBaseError {
30
+ code = "UNAUTHORIZED";
31
+ }
32
+ /** An internal error. */
33
+ export class InternalError extends WalletBaseError {
34
+ code = "INTERNAL";
35
+ }
36
+ /** The encryption type of the request is not supported by the wallet service. */
37
+ export class UnsupportedEncryptionError extends WalletBaseError {
38
+ code = "UNSUPPORTED_ENCRYPTION";
39
+ }
40
+ /** The payment failed. This may be due to a timeout, exhausting all routes, insufficient capacity or similar. */
41
+ export class PaymentFailedError extends WalletBaseError {
42
+ code = "PAYMENT_FAILED";
43
+ }
44
+ /** The invoice could not be found by the given parameters. */
45
+ export class NotFoundError extends WalletBaseError {
46
+ code = "NOT_FOUND";
47
+ }
48
+ /** Other error. */
49
+ export class OtherError extends WalletBaseError {
50
+ code = "OTHER";
51
+ }
52
+ /** Factory function to create NWC error instances from error code and message */
53
+ export function createWalletError(code, message) {
54
+ switch (code) {
55
+ case "RATE_LIMITED":
56
+ return new RateLimitedError(message);
57
+ case "NOT_IMPLEMENTED":
58
+ return new NotImplementedError(message);
59
+ case "INSUFFICIENT_BALANCE":
60
+ return new InsufficientBalanceError(message);
61
+ case "QUOTA_EXCEEDED":
62
+ return new QuotaExceededError(message);
63
+ case "RESTRICTED":
64
+ return new RestrictedError(message);
65
+ case "UNAUTHORIZED":
66
+ return new UnauthorizedError(message);
67
+ case "INTERNAL":
68
+ return new InternalError(message);
69
+ case "UNSUPPORTED_ENCRYPTION":
70
+ return new UnsupportedEncryptionError(message);
71
+ case "PAYMENT_FAILED":
72
+ return new PaymentFailedError(message);
73
+ case "NOT_FOUND":
74
+ return new NotFoundError(message);
75
+ case "OTHER":
76
+ return new OtherError(message);
77
+ default:
78
+ // This should never happen with proper typing, but provides a fallback
79
+ return new OtherError(message);
80
+ }
81
+ }
@@ -0,0 +1,26 @@
1
+ import { HiddenContentSigner } from "applesauce-core/helpers";
2
+ import { NostrEvent } from "nostr-tools";
3
+ export declare const WALLET_HISTORY_KIND = 7376;
4
+ export type HistoryDirection = "in" | "out";
5
+ export type HistoryContent = {
6
+ /** The direction of the transaction, in = received, out = sent */
7
+ direction: HistoryDirection;
8
+ /** The amount of the transaction */
9
+ amount: number;
10
+ /** An array of token event ids created */
11
+ created: string[];
12
+ /** The mint that was spent from */
13
+ mint?: string;
14
+ /** The fee paid */
15
+ fee?: number;
16
+ };
17
+ export declare const HistoryContentSymbol: unique symbol;
18
+ /** returns an array of redeemed event ids in a history event */
19
+ export declare function getHistoryRedeemed(history: NostrEvent): string[];
20
+ /** Checks if the history contents are locked */
21
+ export declare function isHistoryContentLocked(history: NostrEvent): boolean;
22
+ /** Returns the parsed content of a 7376 history event */
23
+ export declare function getHistoryContent(history: NostrEvent): HistoryContent | undefined;
24
+ /** Decrypts a wallet history event */
25
+ export declare function unlockHistoryContent(history: NostrEvent, signer: HiddenContentSigner): Promise<HistoryContent>;
26
+ export declare function lockHistoryContent(history: NostrEvent): void;
@@ -0,0 +1,47 @@
1
+ import { getHiddenTags, getOrComputeCachedValue, isETag, isHiddenContentLocked, isHiddenTagsLocked, lockHiddenTags, setHiddenTagsEncryptionMethod, unlockHiddenTags, } from "applesauce-core/helpers";
2
+ export const WALLET_HISTORY_KIND = 7376;
3
+ // Enable hidden content for wallet history kind
4
+ setHiddenTagsEncryptionMethod(WALLET_HISTORY_KIND, "nip44");
5
+ export const HistoryContentSymbol = Symbol.for("history-content");
6
+ /** returns an array of redeemed event ids in a history event */
7
+ export function getHistoryRedeemed(history) {
8
+ return history.tags.filter((t) => isETag(t) && t[3] === "redeemed").map((t) => t[1]);
9
+ }
10
+ /** Checks if the history contents are locked */
11
+ export function isHistoryContentLocked(history) {
12
+ return isHiddenTagsLocked(history);
13
+ }
14
+ /** Returns the parsed content of a 7376 history event */
15
+ export function getHistoryContent(history) {
16
+ if (isHistoryContentLocked(history))
17
+ return undefined;
18
+ return getOrComputeCachedValue(history, HistoryContentSymbol, () => {
19
+ const tags = getHiddenTags(history);
20
+ if (!tags)
21
+ throw new Error("History event is locked");
22
+ const direction = tags.find((t) => t[0] === "direction")?.[1];
23
+ if (!direction)
24
+ throw new Error("History event missing direction");
25
+ const amountStr = tags.find((t) => t[0] === "amount")?.[1];
26
+ if (!amountStr)
27
+ throw new Error("History event missing amount");
28
+ const amount = parseInt(amountStr);
29
+ if (!Number.isFinite(amount))
30
+ throw new Error("Failed to parse amount");
31
+ const mint = tags.find((t) => t[0] === "mint")?.[1];
32
+ const feeStr = tags.find((t) => t[0] === "fee")?.[1];
33
+ const fee = feeStr ? parseInt(feeStr) : undefined;
34
+ const created = tags.filter((t) => isETag(t) && t[3] === "created").map((t) => t[1]);
35
+ return { direction, amount, created, mint, fee };
36
+ });
37
+ }
38
+ /** Decrypts a wallet history event */
39
+ export async function unlockHistoryContent(history, signer) {
40
+ if (isHiddenContentLocked(history))
41
+ await unlockHiddenTags(history, signer);
42
+ return getHistoryContent(history);
43
+ }
44
+ export function lockHistoryContent(history) {
45
+ Reflect.deleteProperty(history, HistoryContentSymbol);
46
+ lockHiddenTags(history);
47
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./connect-uri.js";
2
+ export * from "./encryption.js";
3
+ export * from "./request.js";
4
+ export * from "./response.js";
5
+ export * from "./support.js";
6
+ export * from "./notification.js";
7
+ export * from "./encryption.js";
@@ -0,0 +1,7 @@
1
+ export * from "./connect-uri.js";
2
+ export * from "./encryption.js";
3
+ export * from "./request.js";
4
+ export * from "./response.js";
5
+ export * from "./support.js";
6
+ export * from "./notification.js";
7
+ export * from "./encryption.js";
@@ -0,0 +1,34 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { EncryptionMethods } from "./encryption.js";
3
+ import { WalletMethods } from "./methods.js";
4
+ import { NotificationTypes } from "./notification.js";
5
+ export declare const WALLET_INFO_KIND = 13194;
6
+ /** A symbol used to cache the wallet info on the event */
7
+ export declare const WalletInfoSymbol: unique symbol;
8
+ /** Wallet service capabilities and information */
9
+ export interface WalletFeatures {
10
+ /** List of supported methods for this wallet service */
11
+ methods: WalletMethods[];
12
+ /** List of supported encryption methods */
13
+ encryption: EncryptionMethods[];
14
+ /** List of supported notifications, optional */
15
+ notifications?: NotificationTypes[];
16
+ }
17
+ /** Gets the wallet info from a kind 13194 event */
18
+ export declare function getWalletFeatures(info: NostrEvent): WalletFeatures | null;
19
+ /** Gets the encryption methods from a wallet info event */
20
+ export declare function getEncryptionMethods(info: NostrEvent | WalletFeatures): EncryptionMethods[];
21
+ /** Checks if the wallet service supports a specific encryption method */
22
+ export declare function supportsEncryption(info: NostrEvent | WalletFeatures, encryption: EncryptionMethods): boolean;
23
+ /** Gets the preferred encryption method (nip44_v2 preferred over nip04) */
24
+ export declare function getPreferredEncryption(info: NostrEvent | WalletFeatures): EncryptionMethods;
25
+ /** Checks if the wallet service supports a specific method */
26
+ export declare function supportsMethod(info: NostrEvent | WalletFeatures, method: WalletMethods): boolean;
27
+ /** Checks if the wallet service supports notifications */
28
+ export declare function supportsNotifications(info: NostrEvent | WalletFeatures): boolean;
29
+ /** Checks if the wallet service supports a specific notification type */
30
+ export declare function supportsNotificationType(info: NostrEvent | WalletFeatures, notificationType: NotificationTypes): boolean;
31
+ /** Gets all supported methods from the wallet info */
32
+ export declare function getSupportedMethods(info: NostrEvent | WalletFeatures): WalletMethods[];
33
+ /** Gets all supported notifications from the wallet info */
34
+ export declare function getSupportedNotifications(info: NostrEvent | WalletFeatures): NotificationTypes[];
@@ -0,0 +1,97 @@
1
+ import { getOrComputeCachedValue, getTagValue, isEvent } from "applesauce-core/helpers";
2
+ export const WALLET_INFO_KIND = 13194;
3
+ /** A symbol used to cache the wallet info on the event */
4
+ export const WalletInfoSymbol = Symbol("wallet-info");
5
+ /** Gets the wallet info from a kind 13194 event */
6
+ export function getWalletFeatures(info) {
7
+ if (info.kind !== WALLET_INFO_KIND)
8
+ return null;
9
+ return getOrComputeCachedValue(info, WalletInfoSymbol, () => {
10
+ const content = info.content.trim();
11
+ if (!content)
12
+ return null;
13
+ // Parse methods from content (space-separated)
14
+ const contentParts = content.split(/\s+/);
15
+ const methods = contentParts
16
+ .filter((part) => [
17
+ "pay_invoice",
18
+ "multi_pay_invoice",
19
+ "pay_keysend",
20
+ "multi_pay_keysend",
21
+ "make_invoice",
22
+ "lookup_invoice",
23
+ "list_transactions",
24
+ "get_balance",
25
+ "get_info",
26
+ "notifications",
27
+ ].includes(part))
28
+ .filter((part) => part !== "notifications");
29
+ // Parse encryption methods from encryption tag
30
+ const encryptionTag = getTagValue(info, "encryption");
31
+ const encryption = encryptionTag
32
+ ? encryptionTag
33
+ .split(/\s+/)
34
+ .filter((method) => method === "nip44_v2" || method === "nip04")
35
+ : ["nip04"]; // Default to nip04 if no encryption tag is present
36
+ // Parse notifications from notifications tag
37
+ const notificationsTag = getTagValue(info, "notifications");
38
+ const notifications = notificationsTag
39
+ ? notificationsTag
40
+ .split(/\s+/)
41
+ .filter((notif) => notif === "payment_received" || notif === "payment_sent")
42
+ : undefined;
43
+ return {
44
+ methods,
45
+ encryption,
46
+ notifications,
47
+ };
48
+ });
49
+ }
50
+ /** Gets the encryption methods from a wallet info event */
51
+ export function getEncryptionMethods(info) {
52
+ const walletInfo = isEvent(info) ? getWalletFeatures(info) : info;
53
+ return walletInfo?.encryption ?? [];
54
+ }
55
+ /** Checks if the wallet service supports a specific encryption method */
56
+ export function supportsEncryption(info, encryption) {
57
+ const encryptionMethods = getEncryptionMethods(info);
58
+ return encryptionMethods.includes(encryption);
59
+ }
60
+ /** Gets the preferred encryption method (nip44_v2 preferred over nip04) */
61
+ export function getPreferredEncryption(info) {
62
+ const encryptionMethods = getEncryptionMethods(info);
63
+ if (encryptionMethods.length === 0)
64
+ return "nip04";
65
+ // Prefer nip44_v2 over nip04
66
+ if (encryptionMethods.includes("nip44_v2"))
67
+ return "nip44_v2";
68
+ if (encryptionMethods.includes("nip04"))
69
+ return "nip04";
70
+ // Absence of this tag implies that the wallet only supports nip04.
71
+ return "nip04";
72
+ }
73
+ /** Checks if the wallet service supports a specific method */
74
+ export function supportsMethod(info, method) {
75
+ const walletInfo = isEvent(info) ? getWalletFeatures(info) : info;
76
+ return walletInfo?.methods.includes(method) ?? false;
77
+ }
78
+ /** Checks if the wallet service supports notifications */
79
+ export function supportsNotifications(info) {
80
+ const walletInfo = isEvent(info) ? getWalletFeatures(info) : info;
81
+ return (walletInfo?.notifications?.length ?? 0) > 0;
82
+ }
83
+ /** Checks if the wallet service supports a specific notification type */
84
+ export function supportsNotificationType(info, notificationType) {
85
+ const walletInfo = isEvent(info) ? getWalletFeatures(info) : info;
86
+ return walletInfo?.notifications?.includes(notificationType) ?? false;
87
+ }
88
+ /** Gets all supported methods from the wallet info */
89
+ export function getSupportedMethods(info) {
90
+ const walletInfo = isEvent(info) ? getWalletFeatures(info) : info;
91
+ return walletInfo?.methods ?? [];
92
+ }
93
+ /** Gets all supported notifications from the wallet info */
94
+ export function getSupportedNotifications(info) {
95
+ const walletInfo = isEvent(info) ? getWalletFeatures(info) : info;
96
+ return walletInfo?.notifications ?? [];
97
+ }
@@ -0,0 +1 @@
1
+ export type WalletMethods = "pay_invoice" | "multi_pay_invoice" | "pay_keysend" | "multi_pay_keysend" | "make_invoice" | "lookup_invoice" | "list_transactions" | "get_balance" | "get_info";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { HiddenContentSigner } from "applesauce-core/helpers";
2
+ import { NostrEvent } from "nostr-tools";
3
+ import { Transaction } from "./response.js";
4
+ export declare const WALLET_NOTIFICATION_KIND = 23197;
5
+ export declare const WALLET_LEGACY_NOTIFICATION_KIND = 23196;
6
+ /** A symbol used to cache the wallet notification on the event */
7
+ export declare const WalletNotificationSymbol: unique symbol;
8
+ /** Supported notification types */
9
+ export type NotificationType = "payment_received" | "payment_sent";
10
+ /** Base notification structure */
11
+ export interface BaseNotification<TNotificationType extends NotificationType, TNotification> {
12
+ /** Indicates the structure of the notification field */
13
+ notification_type: TNotificationType;
14
+ /** Notification data */
15
+ notification: TNotification;
16
+ }
17
+ /** Payment received notification */
18
+ export type PaymentReceivedNotification = BaseNotification<"payment_received", Transaction>;
19
+ /** Payment sent notification */
20
+ export type PaymentSentNotification = BaseNotification<"payment_sent", Transaction>;
21
+ /** Union type for all NIP-47 notification types */
22
+ export type WalletNotification = PaymentReceivedNotification | PaymentSentNotification;
23
+ /** Checks if a kind 23196 or 23197 event is locked */
24
+ export declare function isWalletNotificationLocked(notification: NostrEvent): boolean;
25
+ /** Unlocks a kind 23196 or 23197 event */
26
+ export declare function unlockWalletNotification(notification: NostrEvent, signer: HiddenContentSigner): Promise<WalletNotification | undefined | null>;
27
+ /** Gets the wallet notification from a kind 23196 or 23197 event */
28
+ export declare function getWalletNotification(notification: NostrEvent): WalletNotification | undefined | null;
@@ -0,0 +1,28 @@
1
+ import { getHiddenContent, getOrComputeCachedValue, isHiddenContentLocked, setHiddenContentEncryptionMethod, unlockHiddenContent, } from "applesauce-core/helpers";
2
+ export const WALLET_NOTIFICATION_KIND = 23197;
3
+ export const WALLET_LEGACY_NOTIFICATION_KIND = 23196;
4
+ /** A symbol used to cache the wallet notification on the event */
5
+ export const WalletNotificationSymbol = Symbol("wallet-notification");
6
+ // Setup the encryption method to use for notification kinds
7
+ setHiddenContentEncryptionMethod(WALLET_NOTIFICATION_KIND, "nip44");
8
+ setHiddenContentEncryptionMethod(WALLET_LEGACY_NOTIFICATION_KIND, "nip04");
9
+ /** Checks if a kind 23196 or 23197 event is locked */
10
+ export function isWalletNotificationLocked(notification) {
11
+ return isHiddenContentLocked(notification);
12
+ }
13
+ /** Unlocks a kind 23196 or 23197 event */
14
+ export async function unlockWalletNotification(notification, signer) {
15
+ await unlockHiddenContent(notification, signer);
16
+ return getWalletNotification(notification);
17
+ }
18
+ /** Gets the wallet notification from a kind 23196 or 23197 event */
19
+ export function getWalletNotification(notification) {
20
+ if (isWalletNotificationLocked(notification))
21
+ return undefined;
22
+ return getOrComputeCachedValue(notification, WalletNotificationSymbol, () => {
23
+ const content = getHiddenContent(notification);
24
+ if (!content)
25
+ return null;
26
+ return JSON.parse(content);
27
+ });
28
+ }