applesauce-wallet 2.0.0 → 3.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 (49) hide show
  1. package/dist/actions/index.d.ts +2 -0
  2. package/dist/actions/index.js +2 -0
  3. package/dist/actions/zap-info.d.ts +22 -0
  4. package/dist/actions/zap-info.js +83 -0
  5. package/dist/actions/zaps.d.ts +8 -0
  6. package/dist/actions/zaps.js +30 -0
  7. package/dist/blueprints/history.js +1 -1
  8. package/dist/blueprints/index.d.ts +3 -1
  9. package/dist/blueprints/index.js +3 -1
  10. package/dist/blueprints/tokens.js +2 -2
  11. package/dist/blueprints/wallet.js +3 -5
  12. package/dist/blueprints/zaps.d.ts +8 -0
  13. package/dist/blueprints/zaps.js +12 -0
  14. package/dist/helpers/index.d.ts +5 -3
  15. package/dist/helpers/index.js +5 -3
  16. package/dist/helpers/nutzap.d.ts +27 -0
  17. package/dist/helpers/nutzap.js +66 -0
  18. package/dist/helpers/zap-info.d.ts +19 -0
  19. package/dist/helpers/zap-info.js +42 -0
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/models/index.d.ts +2 -1
  23. package/dist/models/index.js +2 -1
  24. package/dist/models/nutzap.d.ts +6 -0
  25. package/dist/models/nutzap.js +16 -0
  26. package/dist/operations/{event/history.d.ts → history.d.ts} +2 -2
  27. package/dist/operations/history.js +34 -0
  28. package/dist/operations/index.d.ts +5 -2
  29. package/dist/operations/index.js +5 -2
  30. package/dist/operations/nutzap.d.ts +14 -0
  31. package/dist/operations/nutzap.js +33 -0
  32. package/dist/operations/{event/tokens.d.ts → tokens.d.ts} +1 -1
  33. package/dist/operations/{event/tokens.js → tokens.js} +2 -2
  34. package/dist/operations/wallet.d.ts +8 -0
  35. package/dist/operations/wallet.js +30 -0
  36. package/dist/operations/zap-info.d.ts +10 -0
  37. package/dist/operations/zap-info.js +17 -0
  38. package/package.json +10 -15
  39. package/dist/operations/event/history.js +0 -19
  40. package/dist/operations/event/index.d.ts +0 -3
  41. package/dist/operations/event/index.js +0 -3
  42. package/dist/operations/event/wallet.d.ts +0 -4
  43. package/dist/operations/event/wallet.js +0 -14
  44. package/dist/operations/tag/history.d.ts +0 -14
  45. package/dist/operations/tag/history.js +0 -34
  46. package/dist/operations/tag/index.d.ts +0 -2
  47. package/dist/operations/tag/index.js +0 -2
  48. package/dist/operations/tag/wallet.d.ts +0 -5
  49. package/dist/operations/tag/wallet.js +0 -15
@@ -1,2 +1,4 @@
1
1
  export * from "./wallet.js";
2
2
  export * from "./tokens.js";
3
+ export * from "./zaps.js";
4
+ export * from "./zap-info.js";
@@ -1,2 +1,4 @@
1
1
  export * from "./wallet.js";
2
2
  export * from "./tokens.js";
3
+ export * from "./zaps.js";
4
+ export * from "./zap-info.js";
@@ -0,0 +1,22 @@
1
+ import { Action } from "applesauce-actions";
2
+ /** An action to add a relay to the kind 10019 nutzap info event */
3
+ export declare function AddNutzapInfoRelay(relay: string | string[]): Action;
4
+ /** An action to remove a relay from the kind 10019 nutzap info event */
5
+ export declare function RemoveNutzapInfoRelay(relay: string | string[]): Action;
6
+ /** An action to add a mint to the kind 10019 nutzap info event */
7
+ export declare function AddNutzapInfoMint(mint: {
8
+ url: string;
9
+ units?: string[];
10
+ } | Array<{
11
+ url: string;
12
+ units?: string[];
13
+ }>): Action;
14
+ /** An action to remove a mint from the kind 10019 nutzap info event */
15
+ export declare function RemoveNutzapInfoMint(mint: string | string[]): Action;
16
+ /** An action to set the pubkey for the kind 10019 nutzap info event */
17
+ export declare function SetNutzapInfoPubkey(pubkey: string): Action;
18
+ /** An action to update the entire nutzap info event */
19
+ export declare function UpdateNutzapInfo(relays: string[], mints: Array<{
20
+ url: string;
21
+ units?: string[];
22
+ }>, pubkey: string): Action;
@@ -0,0 +1,83 @@
1
+ import { modifyPublicTags } from "applesauce-factory/operations";
2
+ import { addNameValueTag, removeNameValueTag, setSingletonTag } from "applesauce-factory/operations/tag";
3
+ import { NUTZAP_INFO_KIND } from "../helpers/zap-info.js";
4
+ import { setNutzapInfoMints, setNutzapInfoPubkey, setNutzapInfoRelays } from "../operations/zap-info.js";
5
+ /** An action to add a relay to the kind 10019 nutzap info event */
6
+ export function AddNutzapInfoRelay(relay) {
7
+ return async function* ({ events, factory, self }) {
8
+ if (typeof relay === "string")
9
+ relay = [relay];
10
+ const operations = relay.map((r) => addNameValueTag(["relay", r], false));
11
+ const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
12
+ const draft = nutzapInfo
13
+ ? await factory.modifyTags(nutzapInfo, ...operations)
14
+ : await factory.build({ kind: NUTZAP_INFO_KIND }, modifyPublicTags(...operations));
15
+ const signed = await factory.sign(draft);
16
+ yield signed;
17
+ };
18
+ }
19
+ /** An action to remove a relay from the kind 10019 nutzap info event */
20
+ export function RemoveNutzapInfoRelay(relay) {
21
+ return async function* ({ events, factory, self }) {
22
+ if (typeof relay === "string")
23
+ relay = [relay];
24
+ const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
25
+ if (!nutzapInfo)
26
+ return;
27
+ const draft = await factory.modifyTags(nutzapInfo, ...relay.map((r) => removeNameValueTag(["relay", r])));
28
+ const signed = await factory.sign(draft);
29
+ yield signed;
30
+ };
31
+ }
32
+ /** An action to add a mint to the kind 10019 nutzap info event */
33
+ export function AddNutzapInfoMint(mint) {
34
+ return async function* ({ events, factory, self }) {
35
+ const mints = Array.isArray(mint) ? mint : [mint];
36
+ const operations = mints.map((m) => {
37
+ const tag = m.units ? ["mint", m.url, ...m.units] : ["mint", m.url];
38
+ return addNameValueTag(tag, false);
39
+ });
40
+ const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
41
+ const draft = nutzapInfo
42
+ ? await factory.modifyTags(nutzapInfo, ...operations)
43
+ : await factory.build({ kind: NUTZAP_INFO_KIND }, modifyPublicTags(...operations));
44
+ const signed = await factory.sign(draft);
45
+ yield signed;
46
+ };
47
+ }
48
+ /** An action to remove a mint from the kind 10019 nutzap info event */
49
+ export function RemoveNutzapInfoMint(mint) {
50
+ return async function* ({ events, factory, self }) {
51
+ if (typeof mint === "string")
52
+ mint = [mint];
53
+ const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
54
+ if (!nutzapInfo)
55
+ return;
56
+ const draft = await factory.modifyTags(nutzapInfo, ...mint.map((m) => removeNameValueTag(["mint", m])));
57
+ const signed = await factory.sign(draft);
58
+ yield signed;
59
+ };
60
+ }
61
+ /** An action to set the pubkey for the kind 10019 nutzap info event */
62
+ export function SetNutzapInfoPubkey(pubkey) {
63
+ return async function* ({ events, factory, self }) {
64
+ const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
65
+ const draft = nutzapInfo
66
+ ? await factory.modifyTags(nutzapInfo, setSingletonTag(["pubkey", pubkey], true))
67
+ : await factory.build({ kind: NUTZAP_INFO_KIND }, modifyPublicTags(setSingletonTag(["pubkey", pubkey], true)));
68
+ const signed = await factory.sign(draft);
69
+ yield signed;
70
+ };
71
+ }
72
+ /** An action to update the entire nutzap info event */
73
+ export function UpdateNutzapInfo(relays, mints, pubkey) {
74
+ return async function* ({ events, factory, self }) {
75
+ const operations = [setNutzapInfoRelays(relays), setNutzapInfoMints(mints), setNutzapInfoPubkey(pubkey)];
76
+ const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
77
+ const draft = nutzapInfo
78
+ ? await factory.modify(nutzapInfo, ...operations)
79
+ : await factory.build({ kind: NUTZAP_INFO_KIND }, ...operations);
80
+ const signed = await factory.sign(draft);
81
+ yield signed;
82
+ };
83
+ }
@@ -0,0 +1,8 @@
1
+ import { Token } from "@cashu/cashu-ts";
2
+ import { Action } from "applesauce-actions";
3
+ import { NostrEvent } from "nostr-tools";
4
+ import { ProfilePointer } from "nostr-tools/nip19";
5
+ /** Creates a NIP-61 nutzap event for an event with a token */
6
+ export declare function NutzapEvent(event: NostrEvent, token: Token, comment?: string): Action;
7
+ /** Creates a NIP-61 nutzap event to a users profile */
8
+ export declare function NutzapProfile(user: string | ProfilePointer, token: Token, comment?: string): Action;
@@ -0,0 +1,30 @@
1
+ import { NutzapBlueprint, ProfileNutzapBlueprint } from "../blueprints/zaps.js";
2
+ import { NUTZAP_INFO_KIND, verifyProofsLocked } from "../helpers/zap-info.js";
3
+ /** Creates a NIP-61 nutzap event for an event with a token */
4
+ export function NutzapEvent(event, token, comment) {
5
+ return async function* ({ events, factory }) {
6
+ const recipient = event.pubkey;
7
+ const info = events.getReplaceable(NUTZAP_INFO_KIND, recipient);
8
+ if (!info)
9
+ throw new Error("Nutzap info not found");
10
+ // Verify all tokens are p2pk locked
11
+ verifyProofsLocked(token.proofs, info);
12
+ // NOTE: Disabled because mints and units should be checked by the app before
13
+ // const mints = getNutzapInfoMints(info);
14
+ // if (!mints.some((m) => m.mint === token.mint)) throw new Error("Token mint not found in nutzap info");
15
+ const nutzap = await factory.sign(await factory.create(NutzapBlueprint, event, token, comment || token.memo));
16
+ yield nutzap;
17
+ };
18
+ }
19
+ /** Creates a NIP-61 nutzap event to a users profile */
20
+ export function NutzapProfile(user, token, comment) {
21
+ return async function* ({ events, factory }) {
22
+ const info = events.getReplaceable(NUTZAP_INFO_KIND, typeof user === "string" ? user : user.pubkey);
23
+ if (!info)
24
+ throw new Error("Nutzap info not found");
25
+ // Verify all tokens are p2pk locked
26
+ verifyProofsLocked(token.proofs, info);
27
+ const nutzap = await factory.sign(await factory.create(ProfileNutzapBlueprint, user, token, comment || token.memo));
28
+ yield nutzap;
29
+ };
30
+ }
@@ -1,6 +1,6 @@
1
1
  import { blueprint } from "applesauce-factory";
2
2
  import { WALLET_HISTORY_KIND } from "../helpers/history.js";
3
- import { setHistoryContent, setHistoryRedeemed } from "../operations/event/history.js";
3
+ import { setHistoryContent, setHistoryRedeemed } from "../operations/history.js";
4
4
  /** A blueprint that creates a wallet history event */
5
5
  export function WalletHistoryBlueprint(content, redeemed) {
6
6
  return blueprint(WALLET_HISTORY_KIND,
@@ -1,2 +1,4 @@
1
- export * from "./wallet.js";
1
+ export * from "./history.js";
2
2
  export * from "./tokens.js";
3
+ export * from "./wallet.js";
4
+ export * from "./zaps.js";
@@ -1,2 +1,4 @@
1
- export * from "./wallet.js";
1
+ export * from "./history.js";
2
2
  export * from "./tokens.js";
3
+ export * from "./wallet.js";
4
+ export * from "./zaps.js";
@@ -1,11 +1,11 @@
1
1
  import { blueprint } from "applesauce-factory";
2
2
  import { WALLET_TOKEN_KIND } from "../helpers/tokens.js";
3
- import { setTokenContent } from "../operations/event/tokens.js";
3
+ import { setToken } from "../operations/tokens.js";
4
4
  /**
5
5
  * A blueprint for a wallet token event, takes a cashu token and previous deleted token event ids
6
6
  * @param token the cashu token to store
7
7
  * @param [del=[]] an array of previous token event ids that are deleted
8
8
  */
9
9
  export function WalletTokenBlueprint(token, del = []) {
10
- return blueprint(WALLET_TOKEN_KIND, setTokenContent(token, del));
10
+ return blueprint(WALLET_TOKEN_KIND, setToken(token, del));
11
11
  }
@@ -1,13 +1,11 @@
1
1
  import { blueprint } from "applesauce-factory";
2
- import { modifyHiddenTags } from "applesauce-factory/operations/event";
3
2
  import { WALLET_BACKUP_KIND, WALLET_KIND } from "../helpers/wallet.js";
4
- import { setWalletBackupContent } from "../operations/event/wallet.js";
5
- import { setMintTags, setPrivateKeyTag } from "../operations/tag/wallet.js";
3
+ import { setBackupContent, setMints, setPrivateKey } from "../operations/wallet.js";
6
4
  /** A blueprint to create a new 17375 wallet */
7
5
  export function WalletBlueprint(mints, privateKey) {
8
- return blueprint(WALLET_KIND, modifyHiddenTags(privateKey ? setPrivateKeyTag(privateKey) : undefined, setMintTags(mints)));
6
+ return blueprint(WALLET_KIND, setMints(mints), privateKey ? setPrivateKey(privateKey) : undefined);
9
7
  }
10
8
  /** A blueprint that creates a new 375 wallet backup event */
11
9
  export function WalletBackupBlueprint(wallet) {
12
- return blueprint(WALLET_BACKUP_KIND, setWalletBackupContent(wallet));
10
+ return blueprint(WALLET_BACKUP_KIND, setBackupContent(wallet));
13
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
+ }
@@ -1,4 +1,6 @@
1
- export * from "./wallet.js";
2
- export * from "./tokens.js";
3
- export * from "./history.js";
4
1
  export * from "./animated-qr.js";
2
+ export * from "./history.js";
3
+ export * from "./tokens.js";
4
+ export * from "./wallet.js";
5
+ export * from "./zap-info.js";
6
+ export * from "./nutzap.js";
@@ -1,4 +1,6 @@
1
- export * from "./wallet.js";
2
- export * from "./tokens.js";
3
- export * from "./history.js";
4
1
  export * from "./animated-qr.js";
2
+ export * from "./history.js";
3
+ export * from "./tokens.js";
4
+ export * from "./wallet.js";
5
+ export * from "./zap-info.js";
6
+ export * from "./nutzap.js";
@@ -0,0 +1,27 @@
1
+ import { Proof } from "@cashu/cashu-ts";
2
+ import { NostrEvent } from "nostr-tools";
3
+ import { AddressPointer, EventPointer } from "nostr-tools/nip19";
4
+ export declare const NUTZAP_KIND = 9321;
5
+ export declare const NutzapProofsSymbol: unique symbol;
6
+ export declare const NutzapAmountSymbol: unique symbol;
7
+ export declare const NutzapMintSymbol: unique symbol;
8
+ /** Returns the cashu proofs from a kind:9321 nutzap event */
9
+ export declare function getNutzapProofs(event: NostrEvent): Proof[];
10
+ /** Returns the mint URL from a kind:9321 nutzap event */
11
+ export declare function getNutzapMint(event: NostrEvent): string | undefined;
12
+ /** Returns the recipient pubkey from a kind:9321 nutzap event */
13
+ export declare function getNutzapRecipient(event: NostrEvent): string | undefined;
14
+ /** Returns the event ID being nutzapped from a kind:9321 nutzap event */
15
+ export declare function getNutzapEventPointer(event: NostrEvent): EventPointer | undefined;
16
+ /** Returns the event ID being nutzapped from a kind:9321 nutzap event */
17
+ export declare function getNutzapAddressPointer(event: NostrEvent): AddressPointer | undefined;
18
+ /** Returns the EventPointer or AddressPointer from a kind:9321 nutzap event */
19
+ export declare function getNutzapPointer(event: NostrEvent): EventPointer | AddressPointer | undefined;
20
+ /** Returns the comment from a kind:9321 nutzap event */
21
+ export declare function getNutzapComment(event: NostrEvent): string | undefined;
22
+ /** Calculates the total amount of sats in a kind:9321 nutzap event */
23
+ export declare function getNutzapAmount(event: NostrEvent): number;
24
+ /** Checks if a nutzap is valid according to NIP-61 requirements */
25
+ export declare function isValidNutzap(nutzap: NostrEvent): boolean;
26
+ /** Checks if a nutzap event has already been redeemed based on kind:7376 wallet history events */
27
+ export declare function isNutzapRedeemed(nutzapId: string, history: NostrEvent[]): boolean;
@@ -0,0 +1,66 @@
1
+ import { getAddressPointerFromATag, getEventPointerFromETag, getOrComputeCachedValue, getTagValue, processTags, safeParse, } from "applesauce-core/helpers";
2
+ import { getHistoryRedeemed } from "./history.js";
3
+ export const NUTZAP_KIND = 9321;
4
+ // Symbols for caching computed values
5
+ export const NutzapProofsSymbol = Symbol.for("nutzap-proofs");
6
+ export const NutzapAmountSymbol = Symbol.for("nutzap-amount");
7
+ export const NutzapMintSymbol = Symbol.for("nutzap-mint");
8
+ /** Returns the cashu proofs from a kind:9321 nutzap event */
9
+ export function getNutzapProofs(event) {
10
+ return getOrComputeCachedValue(event, NutzapProofsSymbol, () => {
11
+ return processTags(event.tags, (tag) => (tag[0] === "proof" ? safeParse(tag[1]) : undefined));
12
+ });
13
+ }
14
+ /** Returns the mint URL from a kind:9321 nutzap event */
15
+ export function getNutzapMint(event) {
16
+ return getOrComputeCachedValue(event, NutzapMintSymbol, () => {
17
+ const url = getTagValue(event, "u");
18
+ return url && URL.canParse(url) ? url : undefined;
19
+ });
20
+ }
21
+ /** Returns the recipient pubkey from a kind:9321 nutzap event */
22
+ export function getNutzapRecipient(event) {
23
+ return getTagValue(event, "p");
24
+ }
25
+ /** Returns the event ID being nutzapped from a kind:9321 nutzap event */
26
+ export function getNutzapEventPointer(event) {
27
+ const tag = event.tags.find((t) => t[0] === "e");
28
+ if (!tag)
29
+ return;
30
+ return getEventPointerFromETag(tag);
31
+ }
32
+ /** Returns the event ID being nutzapped from a kind:9321 nutzap event */
33
+ export function getNutzapAddressPointer(event) {
34
+ const tag = event.tags.find((t) => t[0] === "a");
35
+ if (!tag)
36
+ return;
37
+ return getAddressPointerFromATag(tag);
38
+ }
39
+ /** Returns the EventPointer or AddressPointer from a kind:9321 nutzap event */
40
+ export function getNutzapPointer(event) {
41
+ return getNutzapEventPointer(event) ?? getNutzapAddressPointer(event);
42
+ }
43
+ /** Returns the comment from a kind:9321 nutzap event */
44
+ export function getNutzapComment(event) {
45
+ return event.content || undefined;
46
+ }
47
+ /** Calculates the total amount of sats in a kind:9321 nutzap event */
48
+ export function getNutzapAmount(event) {
49
+ return getOrComputeCachedValue(event, NutzapAmountSymbol, () => {
50
+ const proofs = getNutzapProofs(event);
51
+ return proofs.reduce((total, proof) => total + (proof.amount || 0), 0);
52
+ });
53
+ }
54
+ /** Checks if a nutzap is valid according to NIP-61 requirements */
55
+ export function isValidNutzap(nutzap) {
56
+ const mint = getNutzapMint(nutzap);
57
+ const recipient = getNutzapRecipient(nutzap);
58
+ const proofs = getNutzapProofs(nutzap);
59
+ if (!mint || !recipient || proofs.length === 0)
60
+ return false;
61
+ return true;
62
+ }
63
+ /** Checks if a nutzap event has already been redeemed based on kind:7376 wallet history events */
64
+ export function isNutzapRedeemed(nutzapId, history) {
65
+ return history.some((entry) => getHistoryRedeemed(entry).includes(nutzapId));
66
+ }
@@ -0,0 +1,19 @@
1
+ import { Proof } from "@cashu/cashu-ts";
2
+ import { NostrEvent } from "nostr-tools";
3
+ export declare const NUTZAP_INFO_KIND = 10019;
4
+ export declare const NutzapMintsSymbol: unique symbol;
5
+ export declare const NutzapRelaysSymbol: unique symbol;
6
+ /** Returns the relay URLs from a kind:10019 nutzap info event */
7
+ export declare function getNutzapInfoRelays(event: NostrEvent): string[];
8
+ /** Returns the mint URLs from a kind:10019 nutzap info event */
9
+ export declare function getNutzapInfoMints(event: NostrEvent): {
10
+ mint: string;
11
+ units?: string[];
12
+ }[];
13
+ /** Returns the pubkey for P2PK-locking from a kind:10019 nutzap info event */
14
+ export declare function getNutzapInfoPubkey(event: NostrEvent): string | undefined;
15
+ /**
16
+ * verfies if proofs are locked to nutzap info
17
+ * @throws {Error} if proofs are not locked to nutzap info
18
+ */
19
+ export declare function verifyProofsLocked(proofs: Proof[], info: NostrEvent): void;
@@ -0,0 +1,42 @@
1
+ import { getOrComputeCachedValue, mergeRelaySets, safeParse } from "applesauce-core/helpers";
2
+ export const NUTZAP_INFO_KIND = 10019;
3
+ // Symbols for caching computed values
4
+ export const NutzapMintsSymbol = Symbol.for("nutzap-mints");
5
+ export const NutzapRelaysSymbol = Symbol.for("nutzap-relays");
6
+ /** Returns the relay URLs from a kind:10019 nutzap info event */
7
+ export function getNutzapInfoRelays(event) {
8
+ return getOrComputeCachedValue(event, NutzapRelaysSymbol, () => {
9
+ return mergeRelaySets(event.tags.filter((t) => t[0] === "relay").map((t) => t[1]));
10
+ });
11
+ }
12
+ /** Returns the mint URLs from a kind:10019 nutzap info event */
13
+ export function getNutzapInfoMints(event) {
14
+ return getOrComputeCachedValue(event, NutzapMintsSymbol, () => {
15
+ return event.tags.filter((t) => t[0] === "mint").map((t) => ({ mint: t[1], units: t.slice(2).filter(Boolean) }));
16
+ });
17
+ }
18
+ /** Returns the pubkey for P2PK-locking from a kind:10019 nutzap info event */
19
+ export function getNutzapInfoPubkey(event) {
20
+ return event.tags.find((t) => t[0] === "pubkey")?.[1];
21
+ }
22
+ /**
23
+ * verfies if proofs are locked to nutzap info
24
+ * @throws {Error} if proofs are not locked to nutzap info
25
+ */
26
+ export function verifyProofsLocked(proofs, info) {
27
+ const pubkey = getNutzapInfoPubkey(info);
28
+ if (!pubkey)
29
+ throw new Error("Nutzap info must have a pubkey");
30
+ const fullPubkey = pubkey.length === 64 ? `02${pubkey}` : pubkey;
31
+ for (const proof of proofs) {
32
+ const secret = safeParse(proof.secret);
33
+ if (!secret)
34
+ throw new Error(`Cashu token must have a spending condition`);
35
+ if (!Array.isArray(secret))
36
+ throw new Error("Invalid spending condition");
37
+ if (secret[0] !== "P2PK")
38
+ throw new Error("Token proofs must be P2PK locked");
39
+ if (secret[1].data !== fullPubkey)
40
+ throw new Error("Token proofs must be P2PK locked to the recipient's nutzap pubkey");
41
+ }
42
+ }
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export * as Actions from "./actions/index.js";
2
2
  export * as Blueprints from "./blueprints/index.js";
3
3
  export * as Helpers from "./helpers/index.js";
4
4
  export * as Models from "./models/index.js";
5
- export * from "./operations/index.js";
5
+ export * as Operations from "./operations/index.js";
package/dist/index.js CHANGED
@@ -2,4 +2,4 @@ export * as Actions from "./actions/index.js";
2
2
  export * as Blueprints from "./blueprints/index.js";
3
3
  export * as Helpers from "./helpers/index.js";
4
4
  export * as Models from "./models/index.js";
5
- export * from "./operations/index.js";
5
+ export * as Operations from "./operations/index.js";
@@ -1,3 +1,4 @@
1
- export * from "./wallet.js";
2
1
  export * from "./history.js";
3
2
  export * from "./tokens.js";
3
+ export * from "./wallet.js";
4
+ export * from "./nutzap.js";
@@ -1,3 +1,4 @@
1
- export * from "./wallet.js";
2
1
  export * from "./history.js";
3
2
  export * from "./tokens.js";
3
+ export * from "./wallet.js";
4
+ export * from "./nutzap.js";
@@ -0,0 +1,6 @@
1
+ import { Model } from "applesauce-core";
2
+ import { NostrEvent } from "nostr-tools";
3
+ /** A model that returns all nutzap events for an event */
4
+ export declare function EventNutZapzModel(event: NostrEvent): Model<NostrEvent[]>;
5
+ /** A model that returns all nutzaps for a users profile */
6
+ export declare function ProfileNutZapzModel(pubkey: string): Model<NostrEvent[]>;
@@ -0,0 +1,16 @@
1
+ import { getReplaceableAddress, isReplaceable } from "applesauce-core/helpers";
2
+ import { map } from "rxjs";
3
+ import { getNutzapPointer, NUTZAP_KIND } from "../helpers/nutzap.js";
4
+ /** A model that returns all nutzap events for an event */
5
+ export function EventNutZapzModel(event) {
6
+ return (events) => isReplaceable(event.kind)
7
+ ? events.timeline({ kinds: [NUTZAP_KIND], "#e": [event.id] })
8
+ : events.timeline({ kinds: [NUTZAP_KIND], "#a": [getReplaceableAddress(event)] });
9
+ }
10
+ /** A model that returns all nutzaps for a users profile */
11
+ export function ProfileNutZapzModel(pubkey) {
12
+ return (events) => events
13
+ .timeline({ kinds: [NUTZAP_KIND], "#p": [pubkey] })
14
+ // filter out nutzaps that are for events
15
+ .pipe(map((zaps) => zaps.filter((zap) => getNutzapPointer(zap) === undefined)));
16
+ }
@@ -1,7 +1,7 @@
1
1
  import { EventOperation } from "applesauce-factory";
2
- import { HistoryContent } from "../../helpers/history.js";
3
2
  import { EventPointer } from "nostr-tools/nip19";
3
+ import { HistoryContent } from "../helpers/history.js";
4
4
  /** Sets the encrypted tags of a wallet history event */
5
5
  export declare function setHistoryContent(content: HistoryContent): EventOperation;
6
- /** Sets the "redeemed" tags on a wallet history event */
6
+ /** Sets the "e" "redeemed" tags on a wallet history event */
7
7
  export declare function setHistoryRedeemed(redeemed: (string | EventPointer)[]): EventOperation;
@@ -0,0 +1,34 @@
1
+ import { ensureMarkedEventPointerTag } from "applesauce-factory/helpers";
2
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations";
3
+ import { setSingletonTag } from "applesauce-factory/operations/tag";
4
+ /** Includes "e" "created" tags in wallet history tags */
5
+ function includeHistoryCreatedTags(created) {
6
+ return (tags) => {
7
+ for (const id of created) {
8
+ tags = ensureMarkedEventPointerTag(tags, typeof id === "string" ? { id } : id, "created");
9
+ }
10
+ return tags;
11
+ };
12
+ }
13
+ /** Sets the encrypted tags of a wallet history event */
14
+ export function setHistoryContent(content) {
15
+ const operations = [
16
+ setSingletonTag(["direction", content.direction], true),
17
+ setSingletonTag(["amount", String(content.amount)], true),
18
+ includeHistoryCreatedTags(content.created),
19
+ ];
20
+ if (content.fee !== undefined)
21
+ operations.push(setSingletonTag(["fee", String(content.fee)], true));
22
+ if (content.mint !== undefined)
23
+ operations.push(setSingletonTag(["mint", content.mint], true));
24
+ return modifyHiddenTags(...operations);
25
+ }
26
+ /** Sets the "e" "redeemed" tags on a wallet history event */
27
+ export function setHistoryRedeemed(redeemed) {
28
+ return modifyPublicTags((tags) => {
29
+ for (const id of redeemed) {
30
+ tags = ensureMarkedEventPointerTag(tags, typeof id === "string" ? { id } : id, "redeemed");
31
+ }
32
+ return tags;
33
+ });
34
+ }
@@ -1,2 +1,5 @@
1
- export * as TagOperations from "./tag/index.js";
2
- export * as EventOperations from "./event/wallet.js";
1
+ export * as History from "./history.js";
2
+ export * as Tokens from "./tokens.js";
3
+ export * as Wallet from "./wallet.js";
4
+ export * as ZapInfo from "./zap-info.js";
5
+ export * as NutZap from "./nutzap.js";
@@ -1,2 +1,5 @@
1
- export * as TagOperations from "./tag/index.js";
2
- export * as EventOperations from "./event/wallet.js";
1
+ export * as History from "./history.js";
2
+ export * as Tokens from "./tokens.js";
3
+ export * as Wallet from "./wallet.js";
4
+ export * as ZapInfo from "./zap-info.js";
5
+ export * as NutZap from "./nutzap.js";
@@ -0,0 +1,14 @@
1
+ import { Proof } from "@cashu/cashu-ts";
2
+ import { EventOperation } from "applesauce-factory";
3
+ import { NostrEvent } from "nostr-tools";
4
+ import { AddressPointer, EventPointer, ProfilePointer } from "nostr-tools/nip19";
5
+ /** Sets the cashu proofs for a nutzap event */
6
+ export declare function setProofs(proofs: Proof[]): EventOperation;
7
+ /** Sets the mint URL for a nutzap event */
8
+ export declare function setMint(mint: string): EventOperation;
9
+ /** Sets the recipient of a nutzap event */
10
+ export declare function setRecipient(recipient: string | ProfilePointer): EventOperation;
11
+ /** Sets the event that is being nutzapped */
12
+ export declare function setEvent(event: EventPointer | AddressPointer | NostrEvent): EventOperation;
13
+ /** Sets the comment content for a nutzap event */
14
+ export declare function setComment(comment: string): EventOperation;
@@ -0,0 +1,33 @@
1
+ import { getReplaceableAddress, isAddressPointer, isEvent, isReplaceable } from "applesauce-core/helpers";
2
+ import { modifyPublicTags } from "applesauce-factory/operations";
3
+ import { setContent } from "applesauce-factory/operations/content";
4
+ import { addAddressTag, addEventTag, addPubkeyTag, setSingletonTag } from "applesauce-factory/operations/tag";
5
+ /** Sets the cashu proofs for a nutzap event */
6
+ export function setProofs(proofs) {
7
+ // Create an operation to append proof tags
8
+ const operation = (tags) => [...tags, ...proofs.map((proof) => ["proof", JSON.stringify(proof)])];
9
+ return modifyPublicTags(operation);
10
+ }
11
+ /** Sets the mint URL for a nutzap event */
12
+ export function setMint(mint) {
13
+ return modifyPublicTags(setSingletonTag(["u", mint]));
14
+ }
15
+ /** Sets the recipient of a nutzap event */
16
+ export function setRecipient(recipient) {
17
+ return modifyPublicTags(addPubkeyTag(recipient, true));
18
+ }
19
+ /** Sets the event that is being nutzapped */
20
+ export function setEvent(event) {
21
+ let operation;
22
+ if (isEvent(event))
23
+ operation = isReplaceable(event.kind) ? addEventTag(event.id) : addAddressTag(getReplaceableAddress(event));
24
+ else if (isAddressPointer(event))
25
+ operation = addAddressTag(event);
26
+ else
27
+ operation = addEventTag(event);
28
+ return modifyPublicTags(operation);
29
+ }
30
+ /** Sets the comment content for a nutzap event */
31
+ export function setComment(comment) {
32
+ return setContent(comment);
33
+ }
@@ -1,4 +1,4 @@
1
1
  import { Token } from "@cashu/cashu-ts";
2
2
  import { EventOperation } from "applesauce-factory";
3
3
  /** Sets the content of a 7375 token event */
4
- export declare function setTokenContent(token: Token, del?: string[]): EventOperation;
4
+ export declare function setToken(token: Token, del?: string[]): EventOperation;
@@ -1,7 +1,7 @@
1
1
  import { EventContentEncryptionMethod } from "applesauce-core/helpers";
2
- import { setEncryptedContent } from "applesauce-factory/operations/event";
2
+ import { setEncryptedContent } from "applesauce-factory/operations/content";
3
3
  /** Sets the content of a 7375 token event */
4
- export function setTokenContent(token, del = []) {
4
+ export function setToken(token, del = []) {
5
5
  return async (draft, ctx) => {
6
6
  if (!ctx.signer)
7
7
  throw new Error(`Missing signer`);
@@ -0,0 +1,8 @@
1
+ import { EventOperation } from "applesauce-factory";
2
+ import { NostrEvent } from "nostr-tools";
3
+ /** Sets the content of a kind 375 wallet backup event */
4
+ export declare function setBackupContent(wallet: NostrEvent): EventOperation;
5
+ /** Sets the "mint" tags in a wallet event */
6
+ export declare function setMints(mints: string[]): EventOperation;
7
+ /** Sets the "privkey" tag on a wallet event */
8
+ export declare function setPrivateKey(privateKey: Uint8Array): EventOperation;
@@ -0,0 +1,30 @@
1
+ import { bytesToHex } from "@noble/hashes/utils";
2
+ import { modifyHiddenTags } from "applesauce-factory/operations";
3
+ import { setSingletonTag } from "applesauce-factory/operations/tag";
4
+ import { WALLET_KIND } from "../helpers/wallet.js";
5
+ /** Sets the content of a kind 375 wallet backup event */
6
+ export function setBackupContent(wallet) {
7
+ return async (draft, ctx) => {
8
+ if (wallet.kind !== WALLET_KIND)
9
+ throw new Error(`Cant create a wallet backup from kind ${wallet.kind}`);
10
+ if (!wallet.content)
11
+ throw new Error("Wallet missing content");
12
+ const pubkey = await ctx.signer?.getPublicKey();
13
+ if (wallet.pubkey !== pubkey)
14
+ throw new Error("Wallet pubkey dose not match signer pubkey");
15
+ return { ...draft, content: wallet.content };
16
+ };
17
+ }
18
+ /** Sets the "mint" tags in a wallet event */
19
+ export function setMints(mints) {
20
+ return modifyHiddenTags((tags) => [
21
+ // remove all existing mint tags
22
+ ...tags.filter((t) => t[0] !== "mint"),
23
+ // add new mint tags
24
+ ...mints.map((mint) => ["mint", mint]),
25
+ ]);
26
+ }
27
+ /** Sets the "privkey" tag on a wallet event */
28
+ export function setPrivateKey(privateKey) {
29
+ return modifyHiddenTags(setSingletonTag(["privkey", bytesToHex(privateKey)], true));
30
+ }
@@ -0,0 +1,10 @@
1
+ import { EventOperation } from "applesauce-factory";
2
+ /** Sets the relays for a nutzap info event */
3
+ export declare function setNutzapInfoRelays(relays: string[]): EventOperation;
4
+ /** Sets the mints for a nutzap info event */
5
+ export declare function setNutzapInfoMints(mints: Array<{
6
+ url: string;
7
+ units?: string[];
8
+ }>): EventOperation;
9
+ /** Sets the pubkey for a nutzap info event */
10
+ export declare function setNutzapInfoPubkey(pubkey: string): EventOperation;
@@ -0,0 +1,17 @@
1
+ import { modifyPublicTags } from "applesauce-factory/operations";
2
+ import { addNameValueTag, setSingletonTag } from "applesauce-factory/operations/tag";
3
+ /** Sets the relays for a nutzap info event */
4
+ export function setNutzapInfoRelays(relays) {
5
+ return modifyPublicTags(...relays.map((relay) => addNameValueTag(["relay", relay], false)));
6
+ }
7
+ /** Sets the mints for a nutzap info event */
8
+ export function setNutzapInfoMints(mints) {
9
+ return modifyPublicTags(...mints.map((mint) => {
10
+ const tag = mint.units ? ["mint", mint.url, ...mint.units] : ["mint", mint.url];
11
+ return addNameValueTag(tag, false);
12
+ }));
13
+ }
14
+ /** Sets the pubkey for a nutzap info event */
15
+ export function setNutzapInfoPubkey(pubkey) {
16
+ return modifyPublicTags(setSingletonTag(["pubkey", pubkey], true));
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-wallet",
3
- "version": "2.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -55,15 +55,10 @@
55
55
  "require": "./dist/operations/index.js",
56
56
  "types": "./dist/operations/index.d.ts"
57
57
  },
58
- "./operations/tag/*": {
59
- "import": "./dist/operations/tag/*.js",
60
- "require": "./dist/operations/tag/*.js",
61
- "types": "./dist/operations/tag/*.d.ts"
62
- },
63
- "./operations/event/*": {
64
- "import": "./dist/operations/event/*.js",
65
- "require": "./dist/operations/event/*.js",
66
- "types": "./dist/operations/event/*.d.ts"
58
+ "./operations/*": {
59
+ "import": "./dist/operations/*.js",
60
+ "require": "./dist/operations/*.js",
61
+ "types": "./dist/operations/*.d.ts"
67
62
  },
68
63
  "./actions": {
69
64
  "import": "./dist/actions/index.js",
@@ -80,16 +75,16 @@
80
75
  "@cashu/cashu-ts": "2.0.0-rc1",
81
76
  "@gandlaf21/bc-ur": "^1.1.12",
82
77
  "@noble/hashes": "^1.7.1",
83
- "applesauce-actions": "^2.0.0",
84
- "applesauce-core": "^2.0.0",
85
- "applesauce-factory": "^2.0.0",
86
- "nostr-tools": "^2.13",
78
+ "applesauce-actions": "^3.1.0",
79
+ "applesauce-core": "^3.1.0",
80
+ "applesauce-factory": "^3.1.0",
81
+ "nostr-tools": "~2.15",
87
82
  "rxjs": "^7.8.1"
88
83
  },
89
84
  "devDependencies": {
90
85
  "@hirez_io/observer-spy": "^2.2.0",
91
86
  "@types/debug": "^4.1.12",
92
- "applesauce-signers": "^2.0.0",
87
+ "applesauce-signers": "^3.1.0",
93
88
  "typescript": "^5.8.3",
94
89
  "vitest": "^3.2.3"
95
90
  },
@@ -1,19 +0,0 @@
1
- import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
2
- import { includeHistoryCreatedTags, includeHistoryRedeemedTags, setHistoryAmountTag, setHistoryDirectionTag, setHistoryFeeTag, setHistoryMintTag, } from "../tag/history.js";
3
- /** Sets the encrypted tags of a wallet history event */
4
- export function setHistoryContent(content) {
5
- const operations = [
6
- setHistoryDirectionTag(content.direction),
7
- setHistoryAmountTag(content.amount),
8
- includeHistoryCreatedTags(content.created),
9
- ];
10
- if (content.fee !== undefined)
11
- operations.push(setHistoryFeeTag(content.fee));
12
- if (content.mint !== undefined)
13
- operations.push(setHistoryMintTag(content.mint));
14
- return modifyHiddenTags(...operations);
15
- }
16
- /** Sets the "redeemed" tags on a wallet history event */
17
- export function setHistoryRedeemed(redeemed) {
18
- return modifyPublicTags(includeHistoryRedeemedTags(redeemed));
19
- }
@@ -1,3 +0,0 @@
1
- export * from "./wallet.js";
2
- export * from "./tokens.js";
3
- export * from "./history.js";
@@ -1,3 +0,0 @@
1
- export * from "./wallet.js";
2
- export * from "./tokens.js";
3
- export * from "./history.js";
@@ -1,4 +0,0 @@
1
- import { EventOperation } from "applesauce-factory";
2
- import { NostrEvent } from "nostr-tools";
3
- /** Sets the content of a kind 375 wallet backup event */
4
- export declare function setWalletBackupContent(wallet: NostrEvent): EventOperation;
@@ -1,14 +0,0 @@
1
- import { WALLET_KIND } from "../../helpers/wallet.js";
2
- /** Sets the content of a kind 375 wallet backup event */
3
- export function setWalletBackupContent(wallet) {
4
- return async (draft, ctx) => {
5
- if (wallet.kind !== WALLET_KIND)
6
- throw new Error(`Cant create a wallet backup from kind ${wallet.kind}`);
7
- if (!wallet.content)
8
- throw new Error("Wallet missing content");
9
- const pubkey = await ctx.signer?.getPublicKey();
10
- if (wallet.pubkey !== pubkey)
11
- throw new Error("Wallet pubkey dose not match signer pubkey");
12
- return { ...draft, content: wallet.content };
13
- };
14
- }
@@ -1,14 +0,0 @@
1
- import { TagOperation } from "applesauce-factory";
2
- import { EventPointer } from "nostr-tools/nip19";
3
- import { HistoryDirection } from "../../helpers/history.js";
4
- /** Sets the "direction" tag on wallet history tags */
5
- export declare function setHistoryDirectionTag(direction: HistoryDirection): TagOperation;
6
- /** Sets the "amount" tag on wallet history tags */
7
- export declare function setHistoryAmountTag(amount: number): TagOperation;
8
- /** Sets the "fee" tag in wallet history tags */
9
- export declare function setHistoryFeeTag(fee: number): TagOperation;
10
- export declare function setHistoryMintTag(mint: string): TagOperation;
11
- /** Includes "created" "e" tags in wallet history tags */
12
- export declare function includeHistoryCreatedTags(created: (string | EventPointer)[]): TagOperation;
13
- /** Includes the "redeemed" tags in wallet history tags */
14
- export declare function includeHistoryRedeemedTags(redeemed: (string | EventPointer)[]): TagOperation;
@@ -1,34 +0,0 @@
1
- import { ensureMarkedEventPointerTag, ensureSingletonTag } from "applesauce-factory/helpers";
2
- /** Sets the "direction" tag on wallet history tags */
3
- export function setHistoryDirectionTag(direction) {
4
- return (tags) => ensureSingletonTag(tags, ["direction", direction], true);
5
- }
6
- /** Sets the "amount" tag on wallet history tags */
7
- export function setHistoryAmountTag(amount) {
8
- return (tags) => ensureSingletonTag(tags, ["amount", String(amount)], true);
9
- }
10
- /** Sets the "fee" tag in wallet history tags */
11
- export function setHistoryFeeTag(fee) {
12
- return (tags) => ensureSingletonTag(tags, ["fee", String(fee)], true);
13
- }
14
- export function setHistoryMintTag(mint) {
15
- return (tags) => ensureSingletonTag(tags, ["mint", mint], true);
16
- }
17
- /** Includes "created" "e" tags in wallet history tags */
18
- export function includeHistoryCreatedTags(created) {
19
- return (tags) => {
20
- for (const id of created) {
21
- tags = ensureMarkedEventPointerTag(tags, typeof id === "string" ? { id } : id, "created");
22
- }
23
- return tags;
24
- };
25
- }
26
- /** Includes the "redeemed" tags in wallet history tags */
27
- export function includeHistoryRedeemedTags(redeemed) {
28
- return (tags) => {
29
- for (const id of redeemed) {
30
- tags = ensureMarkedEventPointerTag(tags, typeof id === "string" ? { id } : id, "redeemed");
31
- }
32
- return tags;
33
- };
34
- }
@@ -1,2 +0,0 @@
1
- export * from "./wallet.js";
2
- export * from "./history.js";
@@ -1,2 +0,0 @@
1
- export * from "./wallet.js";
2
- export * from "./history.js";
@@ -1,5 +0,0 @@
1
- import { TagOperation } from "applesauce-factory";
2
- /** Sets the "mint" tags in a wallet event */
3
- export declare function setMintTags(mints: string[]): TagOperation;
4
- /** Sets the "privkey" tag on a wallet event */
5
- export declare function setPrivateKeyTag(privateKey: Uint8Array): TagOperation;
@@ -1,15 +0,0 @@
1
- import { bytesToHex } from "@noble/hashes/utils";
2
- import { ensureSingletonTag } from "applesauce-factory/helpers";
3
- /** Sets the "mint" tags in a wallet event */
4
- export function setMintTags(mints) {
5
- return (tags) => [
6
- // remove all existing mint tags
7
- ...tags.filter((t) => t[0] !== "mint"),
8
- // add new mint tags
9
- ...mints.map((mint) => ["mint", mint]),
10
- ];
11
- }
12
- /** Sets the "privkey" tag on a wallet event */
13
- export function setPrivateKeyTag(privateKey) {
14
- return (tags) => ensureSingletonTag(tags, ["privkey", bytesToHex(privateKey)], true);
15
- }