applesauce-wallet 0.0.0-next-20250315140539 → 0.0.0-next-20250323173930

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,12 +1,15 @@
1
1
  import { describe, it, expect, beforeEach, vitest } from "vitest";
2
+ import { unlockHiddenTags } from "applesauce-core/helpers";
3
+ import { lastValueFrom } from "rxjs";
4
+ import { generateSecretKey } from "nostr-tools";
2
5
  import { EventStore } from "applesauce-core";
3
6
  import { EventFactory } from "applesauce-factory";
4
7
  import { ActionHub } from "applesauce-actions";
8
+ import { bytesToHex } from "@noble/hashes/utils";
5
9
  import { FakeUser } from "../../__tests__/fake-user.js";
6
- import { CreateWallet } from "../wallet.js";
7
- import { WALLET_BACKUP_KIND } from "../../helpers/wallet.js";
8
- import { unlockHiddenTags } from "applesauce-core/helpers";
9
- import { lastValueFrom } from "rxjs";
10
+ import { WalletAddPrivateKey, CreateWallet } from "../wallet.js";
11
+ import { getWalletPrivateKey, unlockWallet, WALLET_BACKUP_KIND } from "../../helpers/wallet.js";
12
+ import { WalletBlueprint } from "../../blueprints/wallet.js";
10
13
  const user = new FakeUser();
11
14
  let events;
12
15
  let factory;
@@ -30,3 +33,24 @@ describe("CreateWallet", () => {
30
33
  expect(hiddenTags).toEqual(expect.arrayContaining([["mint", "https://mint.money.com"]]));
31
34
  });
32
35
  });
36
+ describe("WalletAddPrivateKey", () => {
37
+ it("should add a private key to an existing wallet event without a private key", async () => {
38
+ const walletEvent = await factory.sign(await factory.create(WalletBlueprint, ["https://mint.money.com"]));
39
+ await events.add(walletEvent);
40
+ const privateKey = generateSecretKey();
41
+ const updatedWallet = await lastValueFrom(hub.exec(WalletAddPrivateKey, privateKey));
42
+ await unlockWallet(updatedWallet, user);
43
+ const key = getWalletPrivateKey(updatedWallet);
44
+ expect(key).toBeDefined();
45
+ expect(bytesToHex(key)).toEqual(bytesToHex(privateKey));
46
+ });
47
+ it("should throw an error if a wallet event already has a private key", async () => {
48
+ const walletEvent = await factory.sign(await factory.create(WalletBlueprint, ["https://mint.money.com"], generateSecretKey()));
49
+ await events.add(walletEvent);
50
+ await expect(hub.run(WalletAddPrivateKey, generateSecretKey())).rejects.toThrow("Wallet already has a private key");
51
+ });
52
+ it("should throw an error if the wallet event does not exist", async () => {
53
+ const privateKey = generateSecretKey();
54
+ await expect(hub.run(WalletAddPrivateKey, privateKey)).rejects.toThrow("Wallet does not exist");
55
+ });
56
+ });
@@ -1,6 +1,11 @@
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
+ /**
5
+ * Adds a private key to a wallet event
6
+ * @throws if the wallet does not exist or is locked
7
+ */
8
+ export declare function WalletAddPrivateKey(privateKey: Uint8Array): Action;
4
9
  /** Unlocks the wallet event and optionally the tokens and history events */
5
10
  export declare function UnlockWallet(unlock?: {
6
11
  history?: boolean;
@@ -1,21 +1,42 @@
1
- import { generateSecretKey } from "nostr-tools";
2
- import { isWalletLocked, unlockWallet, WALLET_KIND } from "../helpers/wallet.js";
1
+ import { getWalletMints, getWalletPrivateKey, isWalletLocked, unlockWallet, WALLET_KIND } from "../helpers/wallet.js";
3
2
  import { WalletBackupBlueprint, WalletBlueprint } from "../blueprints/wallet.js";
4
3
  import { isTokenContentLocked, unlockTokenContent, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
5
4
  import { isHistoryContentLocked, unlockHistoryContent, WALLET_HISTORY_KIND } from "../helpers/history.js";
6
5
  /** An action that creates a new 17375 wallet event and 375 wallet backup */
7
- export function CreateWallet(mints, privateKey = generateSecretKey()) {
6
+ export function CreateWallet(mints, privateKey) {
8
7
  return async function* ({ events, factory, self }) {
9
8
  const existing = events.getReplaceable(WALLET_KIND, self);
10
9
  if (existing)
11
10
  throw new Error("Wallet already exists");
12
- const wallet = await factory.sign(await factory.create(WalletBlueprint, privateKey, mints));
11
+ const wallet = await factory.sign(await factory.create(WalletBlueprint, mints, privateKey));
13
12
  const backup = await factory.sign(await factory.create(WalletBackupBlueprint, wallet));
14
13
  // publish the backup first
15
14
  yield backup;
16
15
  yield wallet;
17
16
  };
18
17
  }
18
+ /**
19
+ * Adds a private key to a wallet event
20
+ * @throws if the wallet does not exist or is locked
21
+ */
22
+ export function WalletAddPrivateKey(privateKey) {
23
+ return async function* ({ events, self, factory }) {
24
+ const wallet = events.getReplaceable(WALLET_KIND, self);
25
+ if (!wallet)
26
+ throw new Error("Wallet does not exist");
27
+ if (isWalletLocked(wallet))
28
+ throw new Error("Wallet is locked");
29
+ if (getWalletPrivateKey(wallet))
30
+ throw new Error("Wallet already has a private key");
31
+ const draft = await factory.create(WalletBlueprint, getWalletMints(wallet), privateKey);
32
+ const signed = await factory.sign(draft);
33
+ // create backup event for wallet
34
+ const backup = await factory.sign(await factory.create(WalletBackupBlueprint, signed));
35
+ // publish events
36
+ yield backup;
37
+ yield signed;
38
+ };
39
+ }
19
40
  /** Unlocks the wallet event and optionally the tokens and history events */
20
41
  export function UnlockWallet(unlock) {
21
42
  return async function* ({ events, self, factory }) {
@@ -1,6 +1,6 @@
1
1
  import { EventBlueprint } from "applesauce-factory";
2
2
  import { NostrEvent } from "nostr-tools";
3
3
  /** A blueprint to create a new 17375 wallet */
4
- export declare function WalletBlueprint(privateKey: Uint8Array, mints: string[]): EventBlueprint;
4
+ export declare function WalletBlueprint(mints: string[], privateKey?: Uint8Array): EventBlueprint;
5
5
  /** A blueprint that creates a new 375 wallet backup event */
6
6
  export declare function WalletBackupBlueprint(wallet: NostrEvent): EventBlueprint;
@@ -4,8 +4,8 @@ import { WALLET_BACKUP_KIND, WALLET_KIND } from "../helpers/wallet.js";
4
4
  import { setWalletBackupContent } from "../operations/event/wallet.js";
5
5
  import { setMintTags, setPrivateKeyTag } from "../operations/tag/wallet.js";
6
6
  /** A blueprint to create a new 17375 wallet */
7
- export function WalletBlueprint(privateKey, mints) {
8
- return (ctx) => EventFactory.runProcess({ kind: WALLET_KIND }, ctx, modifyHiddenTags(setPrivateKeyTag(privateKey), setMintTags(mints)));
7
+ export function WalletBlueprint(mints, privateKey) {
8
+ return (ctx) => EventFactory.runProcess({ kind: WALLET_KIND }, ctx, modifyHiddenTags(privateKey ? setPrivateKeyTag(privateKey) : undefined, setMintTags(mints)));
9
9
  }
10
10
  /** A blueprint that creates a new 375 wallet backup event */
11
11
  export function WalletBackupBlueprint(wallet) {
@@ -12,4 +12,4 @@ export declare function lockWallet(wallet: NostrEvent): void;
12
12
  /** Returns the wallets mints */
13
13
  export declare function getWalletMints(wallet: NostrEvent): string[];
14
14
  /** Returns the wallets private key as a string */
15
- export declare function getWalletPrivateKey(wallet: NostrEvent): Uint8Array;
15
+ export declare function getWalletPrivateKey(wallet: NostrEvent): Uint8Array | undefined;
@@ -33,8 +33,6 @@ export function getWalletPrivateKey(wallet) {
33
33
  if (!tags)
34
34
  throw new Error("Wallet is locked");
35
35
  const key = tags.find((t) => t[0] === "privkey" && t[1])?.[1];
36
- if (!key)
37
- throw new Error("Wallet missing private key");
38
- return hexToBytes(key);
36
+ return key ? hexToBytes(key) : undefined;
39
37
  });
40
38
  }
@@ -14,12 +14,12 @@ describe("setWalletBackupContent", () => {
14
14
  await expect(setWalletBackupContent(note)({ kind: WALLET_BACKUP_KIND, tags: [], created_at: unixNow(), content: "" }, factory.context)).rejects.toThrow();
15
15
  });
16
16
  it("should throw if pubkey does not match", async () => {
17
- const wallet = await factory.sign(await factory.create(WalletBlueprint, generateSecretKey(), []));
17
+ const wallet = await factory.sign(await factory.create(WalletBlueprint, [], generateSecretKey()));
18
18
  const user2 = new FakeUser();
19
19
  await expect(setWalletBackupContent(wallet)({ kind: WALLET_BACKUP_KIND, tags: [], created_at: unixNow(), content: "" }, { signer: user2 })).rejects.toThrow();
20
20
  });
21
21
  it("should copy the content of the wallet event", async () => {
22
- const wallet = await factory.sign(await factory.create(WalletBlueprint, generateSecretKey(), []));
22
+ const wallet = await factory.sign(await factory.create(WalletBlueprint, [], generateSecretKey()));
23
23
  expect(await setWalletBackupContent(wallet)({ kind: WALLET_BACKUP_KIND, tags: [], created_at: unixNow(), content: "" }, factory.context)).toEqual(expect.objectContaining({ content: wallet.content }));
24
24
  });
25
25
  });
@@ -17,7 +17,7 @@ beforeEach(() => {
17
17
  });
18
18
  describe("WalletQuery", () => {
19
19
  it("it should update when event is unlocked", async () => {
20
- const wallet = await user.signEvent(await factory.create(WalletBlueprint, generateSecretKey(), []));
20
+ const wallet = await user.signEvent(await factory.create(WalletBlueprint, [], generateSecretKey()));
21
21
  lockWallet(wallet);
22
22
  events.add(wallet);
23
23
  const spy = subscribeSpyTo(queries.createQuery(WalletQuery, await user.getPublicKey()));
@@ -6,7 +6,7 @@ export type WalletInfo = {
6
6
  } | {
7
7
  locked: false;
8
8
  event: NostrEvent;
9
- privateKey: Uint8Array;
9
+ privateKey?: Uint8Array;
10
10
  mints: string[];
11
11
  };
12
12
  /** A query to get the state of a NIP-60 wallet */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-wallet",
3
- "version": "0.0.0-next-20250315140539",
3
+ "version": "0.0.0-next-20250323173930",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -80,16 +80,16 @@
80
80
  "@cashu/cashu-ts": "2.0.0-rc1",
81
81
  "@gandlaf21/bc-ur": "^1.1.12",
82
82
  "@noble/hashes": "^1.7.1",
83
- "applesauce-actions": "0.0.0-next-20250315140539",
84
- "applesauce-core": "0.0.0-next-20250315140539",
85
- "applesauce-factory": "0.0.0-next-20250315140539",
83
+ "applesauce-actions": "0.0.0-next-20250323173930",
84
+ "applesauce-core": "^0.12.0",
85
+ "applesauce-factory": "^0.12.0",
86
86
  "nostr-tools": "^2.10.4",
87
87
  "rxjs": "^7.8.1"
88
88
  },
89
89
  "devDependencies": {
90
90
  "@hirez_io/observer-spy": "^2.2.0",
91
91
  "@types/debug": "^4.1.12",
92
- "applesauce-signers": "0.0.0-next-20250315140539",
92
+ "applesauce-signers": "0.0.0-next-20250323173930",
93
93
  "typescript": "^5.7.3",
94
94
  "vitest": "^3.0.5"
95
95
  },