applesauce-wallet 0.0.0-next-20250311125119 → 0.0.0-next-20250311162623

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.
@@ -0,0 +1,10 @@
1
+ import { SimpleSigner } from "applesauce-signers/signers/simple-signer";
2
+ import type { NostrEvent } from "nostr-tools";
3
+ export declare class FakeUser extends SimpleSigner {
4
+ pubkey: string;
5
+ event(data?: Partial<NostrEvent>): NostrEvent;
6
+ note(content?: string, extra?: Partial<NostrEvent>): import("nostr-tools").Event;
7
+ profile(profile: any, extra?: Partial<NostrEvent>): import("nostr-tools").Event;
8
+ contacts(pubkeys?: string[]): import("nostr-tools").Event;
9
+ list(tags?: string[][], extra?: Partial<NostrEvent>): import("nostr-tools").Event;
10
+ }
@@ -0,0 +1,31 @@
1
+ import { unixNow } from "applesauce-core/helpers";
2
+ import { SimpleSigner } from "applesauce-signers/signers/simple-signer";
3
+ import { finalizeEvent, getPublicKey, kinds } from "nostr-tools";
4
+ export class FakeUser extends SimpleSigner {
5
+ pubkey = getPublicKey(this.key);
6
+ event(data) {
7
+ return finalizeEvent({
8
+ kind: data?.kind ?? kinds.ShortTextNote,
9
+ content: data?.content || "",
10
+ created_at: data?.created_at ?? unixNow(),
11
+ tags: data?.tags || [],
12
+ }, this.key);
13
+ }
14
+ note(content = "Hello World", extra) {
15
+ return this.event({ kind: kinds.ShortTextNote, content, ...extra });
16
+ }
17
+ profile(profile, extra) {
18
+ return this.event({ kind: kinds.Metadata, content: JSON.stringify({ ...profile }), ...extra });
19
+ }
20
+ contacts(pubkeys = []) {
21
+ return this.event({ kind: kinds.Contacts, tags: pubkeys.map((p) => ["p", p]) });
22
+ }
23
+ list(tags = [], extra) {
24
+ return this.event({
25
+ kind: kinds.Bookmarksets,
26
+ content: "",
27
+ tags: [["d", String(Math.round(Math.random() * 10000))], ...tags],
28
+ ...extra,
29
+ });
30
+ }
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { describe, it, expect, beforeEach, vitest } from "vitest";
2
+ import { EventStore } from "applesauce-core";
3
+ import { EventFactory } from "applesauce-factory";
4
+ import { ActionHub } from "applesauce-actions";
5
+ 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
+ const user = new FakeUser();
10
+ let events;
11
+ let factory;
12
+ let publish;
13
+ let hub;
14
+ beforeEach(() => {
15
+ events = new EventStore();
16
+ factory = new EventFactory({ signer: user });
17
+ publish = vitest.fn().mockResolvedValue(undefined);
18
+ hub = new ActionHub(events, factory, publish);
19
+ });
20
+ describe("CreateWallet", () => {
21
+ it("should publish a wallet backup event", async () => {
22
+ await hub.run(CreateWallet, ["https://mint.money.com"]);
23
+ expect(publish).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ kind: WALLET_BACKUP_KIND }));
24
+ });
25
+ it("should publish a wallet event with mints", async () => {
26
+ await hub.run(CreateWallet, ["https://mint.money.com"]);
27
+ // @ts-expect-error
28
+ const walletEvent = publish.mock.calls[1][1];
29
+ const hiddenTags = await unlockHiddenTags(walletEvent, user);
30
+ // the second call should be the wallet event
31
+ expect(hiddenTags).toEqual(expect.arrayContaining([["mint", "https://mint.money.com"]]));
32
+ });
33
+ });
@@ -0,0 +1 @@
1
+ export * from "./wallet.js";
@@ -0,0 +1 @@
1
+ export * from "./wallet.js";
@@ -0,0 +1,3 @@
1
+ import { Action } from "applesauce-actions";
2
+ /** An action that creates a new 17375 wallet event and 375 wallet backup */
3
+ export declare function CreateWallet(mints: string[], privateKey?: Uint8Array<ArrayBufferLike>): Action;
@@ -0,0 +1,15 @@
1
+ import { generateSecretKey } from "nostr-tools";
2
+ import { WALLET_KIND } from "../helpers/wallet.js";
3
+ import { WalletBackupBlueprint, WalletBlueprint } from "../blueprints/wallet.js";
4
+ /** An action that creates a new 17375 wallet event and 375 wallet backup */
5
+ export function CreateWallet(mints, privateKey = generateSecretKey()) {
6
+ return async ({ events, factory, self, publish }) => {
7
+ const existing = events.getReplaceable(WALLET_KIND, self);
8
+ if (existing)
9
+ throw new Error("Wallet already exists");
10
+ const wallet = await factory.sign(await factory.create(WalletBlueprint, privateKey, mints));
11
+ const backup = await factory.sign(await factory.create(WalletBackupBlueprint, wallet));
12
+ await publish("Wallet backup", backup);
13
+ await publish("Create wallet", wallet);
14
+ };
15
+ }
@@ -1,3 +1,6 @@
1
1
  import { EventBlueprint } from "applesauce-factory";
2
+ import { NostrEvent } from "nostr-tools";
2
3
  /** A blueprint to create a new 17375 wallet */
3
4
  export declare function WalletBlueprint(privateKey: Uint8Array, mints: string[]): EventBlueprint;
5
+ /** A blueprint that creates a new 375 wallet backup event */
6
+ export declare function WalletBackupBlueprint(wallet: NostrEvent): EventBlueprint;
@@ -1,8 +1,13 @@
1
1
  import { EventFactory } from "applesauce-factory";
2
- import { WALLET_KIND } from "../helpers/wallet.js";
3
2
  import { modifyHiddenTags } from "applesauce-factory/operations/event";
3
+ import { WALLET_BACKUP_KIND, WALLET_KIND } from "../helpers/wallet.js";
4
4
  import { setMintTags, setPrivateKeyTag } from "../operations/index.js";
5
+ import { setWalletBackupContent } from "../operations/wallet.js";
5
6
  /** A blueprint to create a new 17375 wallet */
6
7
  export function WalletBlueprint(privateKey, mints) {
7
8
  return (ctx) => EventFactory.runProcess({ kind: WALLET_KIND }, ctx, modifyHiddenTags(setPrivateKeyTag(privateKey), setMintTags(mints)));
8
9
  }
10
+ /** A blueprint that creates a new 375 wallet backup event */
11
+ export function WalletBackupBlueprint(wallet) {
12
+ return (ctx) => EventFactory.runProcess({ kind: WALLET_BACKUP_KIND }, ctx, setWalletBackupContent(wallet));
13
+ }
@@ -1,6 +1,7 @@
1
1
  import { HiddenContentSigner } from "applesauce-core/helpers";
2
2
  import { NostrEvent } from "nostr-tools";
3
3
  export declare const WALLET_KIND = 17375;
4
+ export declare const WALLET_BACKUP_KIND = 375;
4
5
  export declare const WalletPrivateKeySymbol: unique symbol;
5
6
  export declare const WalletMintsSymbol: unique symbol;
6
7
  /** Returns if a wallet is locked */
@@ -1,5 +1,6 @@
1
1
  import { getHiddenTags, getOrComputeCachedValue, isHiddenTagsLocked, unlockHiddenTags, } from "applesauce-core/helpers";
2
2
  export const WALLET_KIND = 17375;
3
+ export const WALLET_BACKUP_KIND = 375;
3
4
  export const WalletPrivateKeySymbol = Symbol.for("wallet-private-key");
4
5
  export const WalletMintsSymbol = Symbol.for("wallet-mints");
5
6
  /** Returns if a wallet is locked */
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * as Queries from "./queries/index.js";
2
2
  export * as Helpers from "./helpers/index.js";
3
3
  export * as Blueprints from "./blueprints/index.js";
4
+ export * as Actions from "./actions/index.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * as Queries from "./queries/index.js";
2
2
  export * as Helpers from "./helpers/index.js";
3
3
  export * as Blueprints from "./blueprints/index.js";
4
+ export * as Actions from "./actions/index.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { setWalletBackupContent } from "../wallet.js";
3
+ import { FakeUser } from "../../__tests__/fake-user.js";
4
+ import { EventFactory } from "applesauce-factory";
5
+ import { WalletBlueprint } from "../../blueprints/wallet.js";
6
+ import { generateSecretKey } from "nostr-tools";
7
+ import { WALLET_BACKUP_KIND } from "../../helpers/wallet.js";
8
+ import { unixNow } from "applesauce-core/helpers";
9
+ const user = new FakeUser();
10
+ const factory = new EventFactory({ signer: user });
11
+ describe("setWalletBackupContent", () => {
12
+ it("should throw if kind is not wallet kind", async () => {
13
+ const note = user.note();
14
+ await expect(setWalletBackupContent(note)({ kind: WALLET_BACKUP_KIND, tags: [], created_at: unixNow(), content: "" }, factory.context)).rejects.toThrow();
15
+ });
16
+ it("should throw if pubkey does not match", async () => {
17
+ const wallet = await factory.sign(await factory.create(WalletBlueprint, generateSecretKey(), []));
18
+ const user2 = new FakeUser();
19
+ await expect(setWalletBackupContent(wallet)({ kind: WALLET_BACKUP_KIND, tags: [], created_at: unixNow(), content: "" }, { signer: user2 })).rejects.toThrow();
20
+ });
21
+ it("should copy the content of the wallet event", async () => {
22
+ const wallet = await factory.sign(await factory.create(WalletBlueprint, generateSecretKey(), []));
23
+ expect(await setWalletBackupContent(wallet)({ kind: WALLET_BACKUP_KIND, tags: [], created_at: unixNow(), content: "" }, factory.context)).toEqual(expect.objectContaining({ content: wallet.content }));
24
+ });
25
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { setMintTags } from "../wallet.js";
3
+ describe("setMintTags", () => {
4
+ it("should replace existing mint tags", () => {
5
+ expect(setMintTags(["https://mint.com"])([["mint", "https://other.mint.com"]], {})).toEqual(expect.arrayContaining([["mint", "https://mint.com"]]));
6
+ });
7
+ it("should ignore other tags", () => {
8
+ expect(setMintTags(["https://mint.com"])([
9
+ ["mint", "https://other.mint.com"],
10
+ ["privkey", "00000000"],
11
+ ], {})).toEqual(expect.arrayContaining([
12
+ ["mint", "https://mint.com"],
13
+ ["privkey", "00000000"],
14
+ ]));
15
+ });
16
+ });
@@ -2,11 +2,12 @@ import { bytesToHex } from "@noble/hashes/utils";
2
2
  import { ensureSingletonTag } from "applesauce-factory/helpers";
3
3
  /** Sets the "mint" tags in a wallet event */
4
4
  export function setMintTags(mints) {
5
- return (tags) => tags
5
+ return (tags) => [
6
6
  // remove all existing mint tags
7
- .filter((t) => t[0] !== "mint")
7
+ ...tags.filter((t) => t[0] !== "mint"),
8
8
  // add new mint tags
9
- .concat(...mints.map((mint) => ["mint", mint]));
9
+ ...mints.map((mint) => ["mint", mint]),
10
+ ];
10
11
  }
11
12
  /** Sets the "privkey" tag on a wallet event */
12
13
  export function setPrivateKeyTag(privateKey) {
@@ -0,0 +1,4 @@
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;
@@ -0,0 +1,14 @@
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-wallet",
3
- "version": "0.0.0-next-20250311125119",
3
+ "version": "0.0.0-next-20250311162623",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -53,14 +53,16 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@noble/hashes": "^1.7.1",
56
- "applesauce-core": "0.0.0-next-20250311125119",
57
- "applesauce-factory": "0.0.0-next-20250311125119",
56
+ "applesauce-actions": "0.0.0-next-20250311162623",
57
+ "applesauce-core": "0.0.0-next-20250311162623",
58
+ "applesauce-factory": "0.0.0-next-20250311162623",
58
59
  "nostr-tools": "^2.10.4",
59
60
  "rxjs": "^7.8.1"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@hirez_io/observer-spy": "^2.2.0",
63
64
  "@types/debug": "^4.1.12",
65
+ "applesauce-signers": "0.0.0-next-20250311162623",
64
66
  "typescript": "^5.7.3",
65
67
  "vitest": "^3.0.5"
66
68
  },