applesauce-wallet 0.0.0-next-20250311171842 → 0.0.0-next-20250311195656

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.
@@ -1,3 +1,8 @@
1
1
  import { Action } from "applesauce-actions";
2
2
  /** An action that creates a new 17375 wallet event and 375 wallet backup */
3
3
  export declare function CreateWallet(mints: string[], privateKey?: Uint8Array): Action;
4
+ /** Unlocks the wallet event and optionally the tokens and history events */
5
+ export declare function UnlockWallet(unlock?: {
6
+ history?: boolean;
7
+ tokens?: boolean;
8
+ }): Action;
@@ -1,6 +1,8 @@
1
1
  import { generateSecretKey } from "nostr-tools";
2
- import { WALLET_KIND } from "../helpers/wallet.js";
2
+ import { isWalletLocked, unlockWallet, WALLET_KIND } from "../helpers/wallet.js";
3
3
  import { WalletBackupBlueprint, WalletBlueprint } from "../blueprints/wallet.js";
4
+ import { unlockTokenContent, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
5
+ import { unlockHistoryContent, WALLET_HISTORY_KIND } from "../helpers/history.js";
4
6
  /** An action that creates a new 17375 wallet event and 375 wallet backup */
5
7
  export function CreateWallet(mints, privateKey = generateSecretKey()) {
6
8
  return async ({ events, factory, self, publish }) => {
@@ -13,3 +15,26 @@ export function CreateWallet(mints, privateKey = generateSecretKey()) {
13
15
  await publish("Create wallet", wallet);
14
16
  };
15
17
  }
18
+ /** Unlocks the wallet event and optionally the tokens and history events */
19
+ export function UnlockWallet(unlock) {
20
+ return async ({ events, self, factory }) => {
21
+ const signer = factory.context.signer;
22
+ if (!signer)
23
+ throw new Error("Missing signer");
24
+ const wallet = events.getReplaceable(WALLET_KIND, self);
25
+ if (!wallet)
26
+ throw new Error("Wallet does not exist");
27
+ if (!isWalletLocked(wallet))
28
+ await unlockWallet(wallet, signer);
29
+ if (unlock?.tokens) {
30
+ const tokens = events.getTimeline({ kinds: [WALLET_TOKEN_KIND], authors: [self] });
31
+ for (const token of tokens)
32
+ await unlockTokenContent(token, signer);
33
+ }
34
+ if (unlock?.history) {
35
+ const history = events.getTimeline({ kinds: [WALLET_HISTORY_KIND], authors: [self] });
36
+ for (const entry of history)
37
+ await unlockHistoryContent(entry, signer);
38
+ }
39
+ };
40
+ }
@@ -0,0 +1,5 @@
1
+ import { EventPointer } from "nostr-tools/nip19";
2
+ import { HistoryContent } from "../helpers/history.js";
3
+ import { EventBlueprint } from "applesauce-factory";
4
+ /** A blueprint that creates a wallet history event */
5
+ export declare function WalletHistoryBlueprint(content: HistoryContent, redeemed: (string | EventPointer)[]): EventBlueprint;
@@ -0,0 +1,11 @@
1
+ import { WALLET_HISTORY_KIND } from "../helpers/history.js";
2
+ import { EventFactory } from "applesauce-factory";
3
+ import { setHistoryContent, setHistoryRedeemed } from "../operations/event/history.js";
4
+ /** A blueprint that creates a wallet history event */
5
+ export function WalletHistoryBlueprint(content, redeemed) {
6
+ return (ctx) => EventFactory.runProcess({ kind: WALLET_HISTORY_KIND }, ctx,
7
+ // set the encrypted tags on the event
8
+ setHistoryContent(content),
9
+ // set the public redeemed tags
10
+ setHistoryRedeemed(redeemed));
11
+ }
@@ -1 +1,2 @@
1
1
  export * from "./wallet.js";
2
+ export * from "./tokens.js";
@@ -1 +1,2 @@
1
1
  export * from "./wallet.js";
2
+ export * from "./tokens.js";
@@ -0,0 +1,8 @@
1
+ import { Token } from "@cashu/cashu-ts";
2
+ import { EventBlueprint } from "applesauce-factory";
3
+ /**
4
+ * A blueprint for a wallet token event, takes a cashu token and previous deleted token event ids
5
+ * @param token the cashu token to store
6
+ * @param [del=[]] an array of previous token event ids that are deleted
7
+ */
8
+ export declare function WalletTokenBlueprint(token: Token, del?: string[]): EventBlueprint;
@@ -0,0 +1,11 @@
1
+ import { EventFactory } from "applesauce-factory";
2
+ import { WALLET_TOKEN_KIND } from "../helpers/tokens.js";
3
+ import { setTokenContent } from "../operations/event/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 (ctx) => EventFactory.runProcess({ kind: WALLET_TOKEN_KIND }, ctx, setTokenContent(token, del));
11
+ }
@@ -1,9 +1,10 @@
1
1
  import { HiddenContentSigner } from "applesauce-core/helpers";
2
2
  import { NostrEvent } from "nostr-tools";
3
3
  export declare const WALLET_HISTORY_KIND = 7376;
4
- export type HistoryDetails = {
4
+ export type HistoryDirection = "in" | "out";
5
+ export type HistoryContent = {
5
6
  /** The direction of the transaction, in = received, out = sent */
6
- direction: "in" | "out";
7
+ direction: HistoryDirection;
7
8
  /** The amount of the transaction */
8
9
  amount: number;
9
10
  /** An array of token event ids created */
@@ -13,12 +14,12 @@ export type HistoryDetails = {
13
14
  /** The fee paid */
14
15
  fee?: number;
15
16
  };
16
- export declare const HistoryDetailsSymbol: unique symbol;
17
+ export declare const HistoryContentSymbol: unique symbol;
17
18
  /** returns an array of redeemed event ids in a history event */
18
19
  export declare function getHistoryRedeemed(history: NostrEvent): string[];
19
- /** Checks if the history details are locked */
20
- export declare function isHistoryDetailsLocked(history: NostrEvent): boolean;
21
- /** Returns the parsed details of a 7376 history event */
22
- export declare function getHistoryDetails(history: NostrEvent): HistoryDetails;
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;
23
24
  /** Decrypts a wallet history event */
24
- export declare function unlockHistoryDetails(history: NostrEvent, signer: HiddenContentSigner): Promise<HistoryDetails>;
25
+ export declare function unlockHistoryContent(history: NostrEvent, signer: HiddenContentSigner): Promise<HistoryContent>;
@@ -1,17 +1,17 @@
1
1
  import { getHiddenTags, getOrComputeCachedValue, isETag, isHiddenTagsLocked, unlockHiddenTags, } from "applesauce-core/helpers";
2
2
  export const WALLET_HISTORY_KIND = 7376;
3
- export const HistoryDetailsSymbol = Symbol.for("history-details");
3
+ export const HistoryContentSymbol = Symbol.for("history-content");
4
4
  /** returns an array of redeemed event ids in a history event */
5
5
  export function getHistoryRedeemed(history) {
6
6
  return history.tags.filter((t) => isETag(t) && t[3] === "redeemed").map((t) => t[1]);
7
7
  }
8
- /** Checks if the history details are locked */
9
- export function isHistoryDetailsLocked(history) {
8
+ /** Checks if the history contents are locked */
9
+ export function isHistoryContentLocked(history) {
10
10
  return isHiddenTagsLocked(history);
11
11
  }
12
- /** Returns the parsed details of a 7376 history event */
13
- export function getHistoryDetails(history) {
14
- return getOrComputeCachedValue(history, HistoryDetailsSymbol, () => {
12
+ /** Returns the parsed content of a 7376 history event */
13
+ export function getHistoryContent(history) {
14
+ return getOrComputeCachedValue(history, HistoryContentSymbol, () => {
15
15
  const tags = getHiddenTags(history);
16
16
  if (!tags)
17
17
  throw new Error("History event is locked");
@@ -32,7 +32,7 @@ export function getHistoryDetails(history) {
32
32
  });
33
33
  }
34
34
  /** Decrypts a wallet history event */
35
- export async function unlockHistoryDetails(history, signer) {
35
+ export async function unlockHistoryContent(history, signer) {
36
36
  await unlockHiddenTags(history, signer);
37
- return getHistoryDetails(history);
37
+ return getHistoryContent(history);
38
38
  }
@@ -1,7 +1,7 @@
1
1
  import { HiddenContentSigner } from "applesauce-core/helpers";
2
2
  import { NostrEvent } from "nostr-tools";
3
3
  export declare const WALLET_TOKEN_KIND = 7375;
4
- export type TokenDetails = {
4
+ export type TokenContent = {
5
5
  /** Cashu mint for the proofs */
6
6
  mint: string;
7
7
  /** Cashu proofs */
@@ -11,13 +11,15 @@ export type TokenDetails = {
11
11
  C: string;
12
12
  id: string;
13
13
  }[];
14
+ /** The cashu unit */
15
+ unit?: string;
14
16
  /** tokens that were destroyed in the creation of this token (helps on wallet state transitions) */
15
17
  del: string[];
16
18
  };
17
- export declare const TokenDetailsSymbol: unique symbol;
19
+ export declare const TokenContentSymbol: unique symbol;
18
20
  /** Returns the decrypted and parsed details of a 7375 token event */
19
- export declare function getTokenDetails(token: NostrEvent): TokenDetails;
21
+ export declare function getTokenContent(token: NostrEvent): TokenContent;
20
22
  /** Returns if token details are locked */
21
- export declare function isTokenDetailsLocked(token: NostrEvent): boolean;
23
+ export declare function isTokenContentLocked(token: NostrEvent): boolean;
22
24
  /** Decrypts a k:7375 token event */
23
- export declare function unlockTokenDetails(token: NostrEvent, signer: HiddenContentSigner): Promise<TokenDetails>;
25
+ export declare function unlockTokenContent(token: NostrEvent, signer: HiddenContentSigner): Promise<TokenContent>;
@@ -1,9 +1,9 @@
1
1
  import { getHiddenContent, getOrComputeCachedValue, isHiddenContentLocked, unlockHiddenContent, } from "applesauce-core/helpers";
2
2
  export const WALLET_TOKEN_KIND = 7375;
3
- export const TokenDetailsSymbol = Symbol.for("token-details");
3
+ export const TokenContentSymbol = Symbol.for("token-content");
4
4
  /** Returns the decrypted and parsed details of a 7375 token event */
5
- export function getTokenDetails(token) {
6
- return getOrComputeCachedValue(token, TokenDetailsSymbol, () => {
5
+ export function getTokenContent(token) {
6
+ return getOrComputeCachedValue(token, TokenContentSymbol, () => {
7
7
  const plaintext = getHiddenContent(token);
8
8
  if (!plaintext)
9
9
  throw new Error("Token is locked");
@@ -11,19 +11,19 @@ export function getTokenDetails(token) {
11
11
  if (!details.mint)
12
12
  throw new Error("Token missing mint");
13
13
  if (!details.proofs)
14
- details.proofs = [];
14
+ throw new Error("Token missing proofs");
15
15
  if (!details.del)
16
16
  details.del = [];
17
17
  return details;
18
18
  });
19
19
  }
20
20
  /** Returns if token details are locked */
21
- export function isTokenDetailsLocked(token) {
21
+ export function isTokenContentLocked(token) {
22
22
  return isHiddenContentLocked(token);
23
23
  }
24
24
  /** Decrypts a k:7375 token event */
25
- export async function unlockTokenDetails(token, signer) {
25
+ export async function unlockTokenContent(token, signer) {
26
26
  if (isHiddenContentLocked(token))
27
27
  await unlockHiddenContent(token, signer);
28
- return getTokenDetails(token);
28
+ return getTokenContent(token);
29
29
  }
@@ -0,0 +1,7 @@
1
+ import { EventOperation } from "applesauce-factory";
2
+ import { HistoryContent } from "../../helpers/history.js";
3
+ import { EventPointer } from "nostr-tools/nip19";
4
+ /** Sets the encrypted tags of a wallet history event */
5
+ export declare function setHistoryContent(content: HistoryContent): EventOperation;
6
+ /** Sets the "redeemed" tags on a wallet history event */
7
+ export declare function setHistoryRedeemed(redeemed: (string | EventPointer)[]): EventOperation;
@@ -0,0 +1,19 @@
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 +1,3 @@
1
1
  export * from "./wallet.js";
2
+ export * from "./tokens.js";
3
+ export * from "./history.js";
@@ -1 +1,3 @@
1
1
  export * from "./wallet.js";
2
+ export * from "./tokens.js";
3
+ export * from "./history.js";
@@ -0,0 +1,4 @@
1
+ import { Token } from "@cashu/cashu-ts";
2
+ import { EventOperation } from "applesauce-factory";
3
+ /** Sets the content of a 7375 token event */
4
+ export declare function setTokenContent(token: Token, del?: string[]): EventOperation;
@@ -0,0 +1,20 @@
1
+ import { EventContentEncryptionMethod } from "applesauce-core/helpers";
2
+ import { setEncryptedContent } from "applesauce-factory/operations/event";
3
+ /** Sets the content of a 7375 token event */
4
+ export function setTokenContent(token, del = []) {
5
+ return async (draft, ctx) => {
6
+ if (!ctx.signer)
7
+ throw new Error(`Missing signer`);
8
+ const pubkey = await ctx.signer.getPublicKey();
9
+ const method = EventContentEncryptionMethod[draft.kind];
10
+ if (!method)
11
+ throw new Error("Failed to find encryption method");
12
+ const content = {
13
+ mint: token.mint,
14
+ proofs: token.proofs,
15
+ unit: token.unit,
16
+ del,
17
+ };
18
+ return await setEncryptedContent(pubkey, JSON.stringify(content), method)(draft, ctx);
19
+ };
20
+ }
@@ -0,0 +1,14 @@
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;
@@ -0,0 +1,34 @@
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 +1,2 @@
1
1
  export * from "./wallet.js";
2
+ export * from "./history.js";
@@ -1 +1,2 @@
1
1
  export * from "./wallet.js";
2
+ export * from "./history.js";
@@ -1,4 +1,4 @@
1
- import { getHistoryRedeemed, isHistoryDetailsLocked, WALLET_HISTORY_KIND } from "../helpers/history.js";
1
+ import { getHistoryRedeemed, isHistoryContentLocked, WALLET_HISTORY_KIND } from "../helpers/history.js";
2
2
  import { combineLatest, filter, map, scan, startWith } from "rxjs";
3
3
  /** Query that returns an array of redeemed event ids for a wallet */
4
4
  export function WalletRedeemedQuery(pubkey) {
@@ -20,7 +20,7 @@ export function WalletHistoryQuery(pubkey, locked) {
20
20
  if (locked === undefined)
21
21
  return history;
22
22
  else
23
- return history.filter((entry) => isHistoryDetailsLocked(entry) === locked);
23
+ return history.filter((entry) => isHistoryContentLocked(entry) === locked);
24
24
  }));
25
25
  },
26
26
  };
@@ -1,5 +1,5 @@
1
1
  import { combineLatest, filter, map, startWith } from "rxjs";
2
- import { getTokenDetails, isTokenDetailsLocked, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
2
+ import { getTokenContent, isTokenContentLocked, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
3
3
  /** removes deleted events from sorted array */
4
4
  function filterDeleted(tokens) {
5
5
  const deleted = new Set();
@@ -10,11 +10,11 @@ function filterDeleted(tokens) {
10
10
  if (deleted.has(token.id))
11
11
  return false;
12
12
  // skip if token is locked
13
- if (isTokenDetailsLocked(token))
13
+ if (isTokenContentLocked(token))
14
14
  return false;
15
15
  else {
16
16
  // add ids to deleted array
17
- const details = getTokenDetails(token);
17
+ const details = getTokenContent(token);
18
18
  for (const id of details.del)
19
19
  deleted.add(id);
20
20
  }
@@ -35,7 +35,7 @@ export function WalletTokensQuery(pubkey, locked) {
35
35
  if (locked === undefined)
36
36
  return tokens;
37
37
  else
38
- return tokens.filter((t) => isTokenDetailsLocked(t) === locked);
38
+ return tokens.filter((t) => isTokenContentLocked(t) === locked);
39
39
  }),
40
40
  // remove deleted events
41
41
  map(filterDeleted));
@@ -54,7 +54,7 @@ export function WalletBalanceQuery(pubkey) {
54
54
  map(filterDeleted),
55
55
  // map tokens to totals
56
56
  map((tokens) => tokens.reduce((totals, token) => {
57
- const details = getTokenDetails(token);
57
+ const details = getTokenContent(token);
58
58
  const total = details.proofs.reduce((t, p) => t + p.amount, 0);
59
59
  return { ...totals, [details.mint]: (totals[details.mint] ?? 0) + total };
60
60
  }, {})));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-wallet",
3
- "version": "0.0.0-next-20250311171842",
3
+ "version": "0.0.0-next-20250311195656",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -77,17 +77,18 @@
77
77
  }
78
78
  },
79
79
  "dependencies": {
80
+ "@cashu/cashu-ts": "2.0.0-rc1",
80
81
  "@noble/hashes": "^1.7.1",
81
- "applesauce-actions": "0.0.0-next-20250311171842",
82
- "applesauce-core": "0.0.0-next-20250311171842",
83
- "applesauce-factory": "0.0.0-next-20250311171842",
82
+ "applesauce-actions": "0.0.0-next-20250311195656",
83
+ "applesauce-core": "0.0.0-next-20250311195656",
84
+ "applesauce-factory": "0.0.0-next-20250311195656",
84
85
  "nostr-tools": "^2.10.4",
85
86
  "rxjs": "^7.8.1"
86
87
  },
87
88
  "devDependencies": {
88
89
  "@hirez_io/observer-spy": "^2.2.0",
89
90
  "@types/debug": "^4.1.12",
90
- "applesauce-signers": "0.0.0-next-20250311171842",
91
+ "applesauce-signers": "0.0.0-next-20250311195656",
91
92
  "typescript": "^5.7.3",
92
93
  "vitest": "^3.0.5"
93
94
  },