applesauce-wallet 0.0.0-next-20250808173123 β 0.0.0-next-20250828144630
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.
- package/dist/__tests__/exports.test.d.ts +1 -0
- package/dist/__tests__/exports.test.js +16 -0
- package/dist/__tests__/fake-user.d.ts +10 -0
- package/dist/__tests__/fake-user.js +31 -0
- package/dist/actions/__tests__/exports.test.d.ts +1 -0
- package/dist/actions/__tests__/exports.test.js +17 -0
- package/dist/actions/__tests__/tokens.test.d.ts +1 -0
- package/dist/actions/__tests__/tokens.test.js +139 -0
- package/dist/actions/__tests__/wallet.test.d.ts +1 -0
- package/dist/actions/__tests__/wallet.test.js +56 -0
- package/dist/blueprints/__tests__/exports.test.d.ts +1 -0
- package/dist/blueprints/__tests__/exports.test.js +13 -0
- package/dist/helpers/__tests__/animated-qr.test.d.ts +1 -0
- package/dist/helpers/__tests__/animated-qr.test.js +44 -0
- package/dist/helpers/__tests__/exports.test.d.ts +1 -0
- package/dist/helpers/__tests__/exports.test.js +42 -0
- package/dist/helpers/__tests__/tokens.test.d.ts +1 -0
- package/dist/helpers/__tests__/tokens.test.js +127 -0
- package/dist/models/__tests__/exports.test.d.ts +1 -0
- package/dist/models/__tests__/exports.test.js +15 -0
- package/dist/models/__tests__/wallet.test.d.ts +1 -0
- package/dist/models/__tests__/wallet.test.js +28 -0
- package/dist/operations/__tests__/exports.test.d.ts +1 -0
- package/dist/operations/__tests__/exports.test.js +12 -0
- package/dist/operations/__tests__/wallet.test.d.ts +1 -0
- package/dist/operations/__tests__/wallet.test.js +25 -0
- package/dist/operations/event/__tests__/exports.test.d.ts +1 -0
- package/dist/operations/event/__tests__/exports.test.js +14 -0
- package/dist/operations/event/__tests__/wallet.test.d.ts +1 -0
- package/dist/operations/event/__tests__/wallet.test.js +25 -0
- package/dist/operations/event/history.d.ts +7 -0
- package/dist/operations/event/history.js +19 -0
- package/dist/operations/event/index.d.ts +3 -0
- package/dist/operations/event/index.js +3 -0
- package/dist/operations/event/tokens.d.ts +4 -0
- package/dist/operations/event/tokens.js +24 -0
- package/dist/operations/event/wallet.d.ts +4 -0
- package/dist/operations/event/wallet.js +14 -0
- package/dist/operations/events/__tests__/wallet.test.d.ts +1 -0
- package/dist/operations/events/__tests__/wallet.test.js +25 -0
- package/dist/operations/events/index.d.ts +1 -0
- package/dist/operations/events/index.js +1 -0
- package/dist/operations/events/wallet.d.ts +4 -0
- package/dist/operations/events/wallet.js +14 -0
- package/dist/operations/tag/__tests__/exports.test.d.ts +1 -0
- package/dist/operations/tag/__tests__/exports.test.js +18 -0
- package/dist/operations/tag/__tests__/wallet.test.d.ts +1 -0
- package/dist/operations/tag/__tests__/wallet.test.js +16 -0
- package/dist/operations/tag/history.d.ts +14 -0
- package/dist/operations/tag/history.js +34 -0
- package/dist/operations/tag/index.d.ts +2 -0
- package/dist/operations/tag/index.js +2 -0
- package/dist/operations/tag/wallet.d.ts +5 -0
- package/dist/operations/tag/wallet.js +15 -0
- package/dist/operations/tags/__tests__/wallet.test.d.ts +1 -0
- package/dist/operations/tags/__tests__/wallet.test.js +16 -0
- package/dist/operations/tags/index.d.ts +1 -0
- package/dist/operations/tags/index.js +1 -0
- package/dist/operations/tags/wallet.d.ts +5 -0
- package/dist/operations/tags/wallet.js +15 -0
- package/dist/queries/__tests__/wallet.test.d.ts +1 -0
- package/dist/queries/__tests__/wallet.test.js +30 -0
- package/dist/queries/history.d.ts +6 -0
- package/dist/queries/history.js +27 -0
- package/dist/queries/index.d.ts +3 -0
- package/dist/queries/index.js +3 -0
- package/dist/queries/tokens.d.ts +6 -0
- package/dist/queries/tokens.js +64 -0
- package/dist/queries/wallet.d.ts +13 -0
- package/dist/queries/wallet.js +21 -0
- package/package.json +6 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"Actions",
|
|
8
|
+
"Blueprints",
|
|
9
|
+
"EventOperations",
|
|
10
|
+
"Helpers",
|
|
11
|
+
"Models",
|
|
12
|
+
"TagOperations",
|
|
13
|
+
]
|
|
14
|
+
`);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -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,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"CompleteSpend",
|
|
8
|
+
"ConsolidateTokens",
|
|
9
|
+
"CreateWallet",
|
|
10
|
+
"ReceiveToken",
|
|
11
|
+
"RolloverTokens",
|
|
12
|
+
"UnlockWallet",
|
|
13
|
+
"WalletAddPrivateKey",
|
|
14
|
+
]
|
|
15
|
+
`);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,139 @@
|
|
|
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 { CheckStateEnum } from "@cashu/cashu-ts";
|
|
6
|
+
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
7
|
+
import { FakeUser } from "../../__tests__/fake-user.js";
|
|
8
|
+
import { ConsolidateTokens } from "../tokens.js";
|
|
9
|
+
import { WalletTokenBlueprint } from "../../blueprints/tokens.js";
|
|
10
|
+
import { unlockTokenContent, WALLET_TOKEN_KIND } from "../../helpers/tokens.js";
|
|
11
|
+
// Update the mock to allow controlling the states
|
|
12
|
+
const mockCheckProofsStates = vitest.fn();
|
|
13
|
+
vitest.mock("@cashu/cashu-ts", () => ({
|
|
14
|
+
CashuMint: vitest.fn(),
|
|
15
|
+
CashuWallet: vitest.fn().mockImplementation(() => ({
|
|
16
|
+
checkProofsStates: mockCheckProofsStates,
|
|
17
|
+
})),
|
|
18
|
+
CheckStateEnum: { UNSPENT: "UNSPENT", SPENT: "SPENT" },
|
|
19
|
+
}));
|
|
20
|
+
const user = new FakeUser();
|
|
21
|
+
const testMint = "https://mint.test.com";
|
|
22
|
+
let events;
|
|
23
|
+
let factory;
|
|
24
|
+
let hub;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
events = new EventStore();
|
|
27
|
+
factory = new EventFactory({ signer: user });
|
|
28
|
+
hub = new ActionHub(events, factory);
|
|
29
|
+
// Reset the mock before each test
|
|
30
|
+
mockCheckProofsStates.mockReset();
|
|
31
|
+
});
|
|
32
|
+
describe("ConsolidateTokens", () => {
|
|
33
|
+
it("should combine multiple token events into a single event", async () => {
|
|
34
|
+
// Set all proofs to be unspent
|
|
35
|
+
mockCheckProofsStates.mockResolvedValue([{ state: CheckStateEnum.UNSPENT }, { state: CheckStateEnum.UNSPENT }]);
|
|
36
|
+
// Create two token events with different proofs
|
|
37
|
+
const token1 = await factory.sign(await factory.create(WalletTokenBlueprint, {
|
|
38
|
+
mint: testMint,
|
|
39
|
+
proofs: [{ amount: 10, secret: "secret1", C: "C", id: "id" }],
|
|
40
|
+
}));
|
|
41
|
+
const token2 = await factory.sign(await factory.create(WalletTokenBlueprint, {
|
|
42
|
+
mint: testMint,
|
|
43
|
+
proofs: [{ amount: 20, secret: "secret2", C: "C", id: "id" }],
|
|
44
|
+
}));
|
|
45
|
+
// Add tokens to event store
|
|
46
|
+
events.add(token1);
|
|
47
|
+
events.add(token2);
|
|
48
|
+
// Run consolidate action
|
|
49
|
+
const spy = subscribeSpyTo(hub.exec(ConsolidateTokens));
|
|
50
|
+
await spy.onComplete();
|
|
51
|
+
// First event should be the new consolidated token
|
|
52
|
+
expect(spy.getValueAt(0).kind).toBe(WALLET_TOKEN_KIND);
|
|
53
|
+
// Extract token content and verify proofs were combined
|
|
54
|
+
const content = await unlockTokenContent(spy.getValueAt(0), user);
|
|
55
|
+
expect(content.proofs).toHaveLength(2);
|
|
56
|
+
expect(content.proofs).toEqual(expect.arrayContaining([
|
|
57
|
+
expect.objectContaining({ amount: 10, secret: "secret1" }),
|
|
58
|
+
expect.objectContaining({ amount: 20, secret: "secret2" }),
|
|
59
|
+
]));
|
|
60
|
+
expect(content.mint).toBe(testMint);
|
|
61
|
+
});
|
|
62
|
+
it("should handle duplicate proofs", async () => {
|
|
63
|
+
// Set all proofs to be unspent
|
|
64
|
+
mockCheckProofsStates.mockResolvedValue([{ state: CheckStateEnum.UNSPENT }, { state: CheckStateEnum.UNSPENT }]);
|
|
65
|
+
// Create two token events with different proofs
|
|
66
|
+
const token1 = await factory.sign(await factory.create(WalletTokenBlueprint, {
|
|
67
|
+
mint: testMint,
|
|
68
|
+
proofs: [{ amount: 10, secret: "secret1", C: "C", id: "id" }],
|
|
69
|
+
}));
|
|
70
|
+
const token2 = await factory.sign(await factory.create(WalletTokenBlueprint, {
|
|
71
|
+
mint: testMint,
|
|
72
|
+
proofs: [
|
|
73
|
+
{ amount: 20, secret: "secret2", C: "C", id: "id" },
|
|
74
|
+
{ amount: 10, secret: "secret1", C: "C", id: "id" },
|
|
75
|
+
],
|
|
76
|
+
}));
|
|
77
|
+
// Add tokens to event store
|
|
78
|
+
events.add(token1);
|
|
79
|
+
events.add(token2);
|
|
80
|
+
// Run consolidate action
|
|
81
|
+
const spy = subscribeSpyTo(hub.exec(ConsolidateTokens));
|
|
82
|
+
await spy.onComplete();
|
|
83
|
+
// First event should be the new consolidated token
|
|
84
|
+
expect(spy.getValueAt(0).kind).toBe(WALLET_TOKEN_KIND);
|
|
85
|
+
// Extract token content and verify proofs were combined
|
|
86
|
+
const content = await unlockTokenContent(spy.getValueAt(0), user);
|
|
87
|
+
expect(content.proofs).toHaveLength(2);
|
|
88
|
+
expect(content.proofs).toEqual(expect.arrayContaining([
|
|
89
|
+
expect.objectContaining({ amount: 10, secret: "secret1" }),
|
|
90
|
+
expect.objectContaining({ amount: 20, secret: "secret2" }),
|
|
91
|
+
]));
|
|
92
|
+
expect(content.mint).toBe(testMint);
|
|
93
|
+
});
|
|
94
|
+
it("should filter out spent proofs", async () => {
|
|
95
|
+
// Create token events with multiple proofs
|
|
96
|
+
const token1 = await factory.sign(await factory.create(WalletTokenBlueprint, {
|
|
97
|
+
mint: testMint,
|
|
98
|
+
proofs: [
|
|
99
|
+
{ amount: 10, secret: "secret1", C: "C", id: "id" },
|
|
100
|
+
{ amount: 20, secret: "secret2", C: "C", id: "id" },
|
|
101
|
+
],
|
|
102
|
+
}, []));
|
|
103
|
+
const token2 = await factory.sign(await factory.create(WalletTokenBlueprint, {
|
|
104
|
+
mint: testMint,
|
|
105
|
+
proofs: [
|
|
106
|
+
{ amount: 30, secret: "secret3", C: "C", id: "id" },
|
|
107
|
+
{ amount: 40, secret: "secret4", C: "C", id: "id" },
|
|
108
|
+
],
|
|
109
|
+
}, []));
|
|
110
|
+
// Add tokens to event store
|
|
111
|
+
events.add(token1);
|
|
112
|
+
events.add(token2);
|
|
113
|
+
// Mock some proofs as spent
|
|
114
|
+
mockCheckProofsStates.mockResolvedValue([
|
|
115
|
+
{ state: CheckStateEnum.UNSPENT }, // secret1
|
|
116
|
+
{ state: CheckStateEnum.SPENT }, // secret2
|
|
117
|
+
{ state: CheckStateEnum.SPENT }, // secret3
|
|
118
|
+
{ state: CheckStateEnum.UNSPENT }, // secret4
|
|
119
|
+
]);
|
|
120
|
+
// Run consolidate action
|
|
121
|
+
const spy = subscribeSpyTo(hub.exec(ConsolidateTokens));
|
|
122
|
+
await spy.onComplete();
|
|
123
|
+
// Verify the consolidated token only contains unspent proofs
|
|
124
|
+
const content = await unlockTokenContent(spy.getValueAt(0), user);
|
|
125
|
+
expect(content.proofs).toHaveLength(2);
|
|
126
|
+
expect(content.proofs).toEqual(expect.arrayContaining([
|
|
127
|
+
expect.objectContaining({ amount: 10, secret: "secret1" }),
|
|
128
|
+
expect.objectContaining({ amount: 40, secret: "secret4" }),
|
|
129
|
+
]));
|
|
130
|
+
expect(content.mint).toBe(testMint);
|
|
131
|
+
// Verify checkProofsStates was called with all proofs
|
|
132
|
+
expect(mockCheckProofsStates).toHaveBeenCalledWith(expect.arrayContaining([
|
|
133
|
+
expect.objectContaining({ amount: 10, secret: "secret1" }),
|
|
134
|
+
expect.objectContaining({ amount: 20, secret: "secret2" }),
|
|
135
|
+
expect.objectContaining({ amount: 30, secret: "secret3" }),
|
|
136
|
+
expect.objectContaining({ amount: 40, secret: "secret4" }),
|
|
137
|
+
]));
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
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";
|
|
5
|
+
import { EventStore } from "applesauce-core";
|
|
6
|
+
import { EventFactory } from "applesauce-factory";
|
|
7
|
+
import { ActionHub } from "applesauce-actions";
|
|
8
|
+
import { bytesToHex } from "@noble/hashes/utils";
|
|
9
|
+
import { FakeUser } from "../../__tests__/fake-user.js";
|
|
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";
|
|
13
|
+
const user = new FakeUser();
|
|
14
|
+
let events;
|
|
15
|
+
let factory;
|
|
16
|
+
let publish;
|
|
17
|
+
let hub;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
events = new EventStore();
|
|
20
|
+
factory = new EventFactory({ signer: user });
|
|
21
|
+
publish = vitest.fn().mockResolvedValue(undefined);
|
|
22
|
+
hub = new ActionHub(events, factory, publish);
|
|
23
|
+
});
|
|
24
|
+
describe("CreateWallet", () => {
|
|
25
|
+
it("should publish a wallet backup event", async () => {
|
|
26
|
+
await hub.run(CreateWallet, ["https://mint.money.com"]);
|
|
27
|
+
expect(publish).toHaveBeenCalledWith(expect.objectContaining({ kind: WALLET_BACKUP_KIND }));
|
|
28
|
+
});
|
|
29
|
+
it("should publish a wallet event with mints", async () => {
|
|
30
|
+
const event = await lastValueFrom(hub.exec(CreateWallet, ["https://mint.money.com"]));
|
|
31
|
+
const hiddenTags = await unlockHiddenTags(event, user);
|
|
32
|
+
// the second call should be the wallet event
|
|
33
|
+
expect(hiddenTags).toEqual(expect.arrayContaining([["mint", "https://mint.money.com"]]));
|
|
34
|
+
});
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"WalletBackupBlueprint",
|
|
8
|
+
"WalletBlueprint",
|
|
9
|
+
"WalletTokenBlueprint",
|
|
10
|
+
]
|
|
11
|
+
`);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { getDecodedToken } from "@cashu/cashu-ts";
|
|
3
|
+
import { lastValueFrom, map, take, timer } from "rxjs";
|
|
4
|
+
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
5
|
+
import { receiveAnimated, sendAnimated } from "../animated-qr.js";
|
|
6
|
+
const tokenStr = "cashuBo2FteBtodHRwczovL3Rlc3RudXQuY2FzaHUuc3BhY2VhdWNzYXRhdIGiYWlIAJofKTJT5B5hcIOkYWEQYXN4QDA2M2VlYjgwZDZjODM4NWU4NzYwODVhN2E4NzdkOTkyY2U4N2QwNTRmY2RjYzNiODMwMzhjOWY3MmNmMDY1ZGVhY1ghAynsxZ-OuZfZDcqYTLPYfCqHO7jkGjn97aolgtSYhYykYWSjYWVYIGbp_9B9aztSlZxz7g6Tqx5M_1PuFzJCVEMOVd8XAF8wYXNYINTOU77ODcj28v04pq7ektdf6sq2XuxvMjVE0wK6jFolYXJYIM_gZnUGT5jDOyZiQ-2vG9zYnuWaY8vPoWGe_3sXvrvbpGFhBGFzeEA0MWMwZDk1YTU5ZjkxNTdlYTc5NTJlNGFlMzYzYTI3NTMxNTllNmQ1NGJiNzExMTg5ZDk5YjU1MmYzYjIzZTJiYWNYIQM-49gen_1nPchxbaAiKprVr78VmMRVpHH_Tu9P8TO5mGFko2FlWCB9j7rlpdBH_m7tNYnLpzPhn-nGmS1CcbUfnPzjxy6G92FzWCDdsby7fGM5324T5UEoV858YWzZ9MCY59KgKP362fJDfmFyWCDL73v4FRo7iMe83bfMuEy3RJPtC1Vr1jdOpw2-x-7EAaRhYQFhc3hANjRhZDI3NmExOGNmNDhiMDZmYjdiMGYwOWFiMTU4ZTA0ZmM0NmIxYzA4YzMyNjJlODUxNzZkYTMzMTgyYzQ3YWFjWCECMDpCbNbrgA9FcQEIYxobU7ik_pTl8sByPqHDmkY4azxhZKNhZVggqHGaff9M270EU8LGxRpG_G4rn2bMgjyk3hFFg78ZXRVhc1ggP6DsNsWykwKE94yZF23gpCyapcoqh6DDZdVu0lKn2Z5hclggmPKig-lObsuxi_1XCm7_Y_tqaCcqEDz8eCwVhJ8gq9M";
|
|
7
|
+
const token = getDecodedToken(tokenStr);
|
|
8
|
+
describe("sendAnimated", () => {
|
|
9
|
+
it("should loop", async () => {
|
|
10
|
+
const qr$ = sendAnimated(token, { interval: 0 });
|
|
11
|
+
const spy = subscribeSpyTo(qr$);
|
|
12
|
+
// wait 100ms
|
|
13
|
+
await lastValueFrom(timer(100));
|
|
14
|
+
// should not have competed
|
|
15
|
+
expect(spy.receivedComplete()).toBeFalsy();
|
|
16
|
+
spy.unsubscribe();
|
|
17
|
+
});
|
|
18
|
+
it("should emit parts", async () => {
|
|
19
|
+
const qr$ = sendAnimated(token, { interval: 0 }).pipe(take(6));
|
|
20
|
+
const spy = subscribeSpyTo(qr$);
|
|
21
|
+
// wait 100ms
|
|
22
|
+
await lastValueFrom(qr$);
|
|
23
|
+
// should not have competed
|
|
24
|
+
expect(spy.getValues()).toEqual(Array(6)
|
|
25
|
+
.fill(0)
|
|
26
|
+
.map((_, i) => expect.stringContaining(`ur:bytes/${i + 1}-6/`)));
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("receiveAnimated", () => {
|
|
30
|
+
it("should decode animated qr", async () => {
|
|
31
|
+
const qr$ = sendAnimated(token, { interval: 0 }).pipe(receiveAnimated, map((part) => (typeof part === "string" ? getDecodedToken(part) : part)));
|
|
32
|
+
const spy = subscribeSpyTo(qr$);
|
|
33
|
+
await lastValueFrom(qr$);
|
|
34
|
+
expect(spy.getValues()).toEqual([
|
|
35
|
+
expect.any(Number),
|
|
36
|
+
expect.any(Number),
|
|
37
|
+
expect.any(Number),
|
|
38
|
+
expect.any(Number),
|
|
39
|
+
expect.any(Number),
|
|
40
|
+
expect.any(Number),
|
|
41
|
+
token,
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"ANIMATED_QR_FRAGMENTS",
|
|
8
|
+
"ANIMATED_QR_INTERVAL",
|
|
9
|
+
"HistoryContentSymbol",
|
|
10
|
+
"TokenContentSymbol",
|
|
11
|
+
"WALLET_BACKUP_KIND",
|
|
12
|
+
"WALLET_HISTORY_KIND",
|
|
13
|
+
"WALLET_KIND",
|
|
14
|
+
"WALLET_TOKEN_KIND",
|
|
15
|
+
"WalletMintsSymbol",
|
|
16
|
+
"WalletPrivateKeySymbol",
|
|
17
|
+
"decodeTokenFromEmojiString",
|
|
18
|
+
"dumbTokenSelection",
|
|
19
|
+
"encodeTokenToEmoji",
|
|
20
|
+
"getHistoryContent",
|
|
21
|
+
"getHistoryRedeemed",
|
|
22
|
+
"getProofUID",
|
|
23
|
+
"getTokenContent",
|
|
24
|
+
"getTokenProofsTotal",
|
|
25
|
+
"getWalletMints",
|
|
26
|
+
"getWalletPrivateKey",
|
|
27
|
+
"ignoreDuplicateProofs",
|
|
28
|
+
"isHistoryContentLocked",
|
|
29
|
+
"isTokenContentLocked",
|
|
30
|
+
"isWalletLocked",
|
|
31
|
+
"lockHistoryContent",
|
|
32
|
+
"lockTokenContent",
|
|
33
|
+
"lockWallet",
|
|
34
|
+
"receiveAnimated",
|
|
35
|
+
"sendAnimated",
|
|
36
|
+
"unlockHistoryContent",
|
|
37
|
+
"unlockTokenContent",
|
|
38
|
+
"unlockWallet",
|
|
39
|
+
]
|
|
40
|
+
`);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { EventFactory } from "applesauce-factory";
|
|
3
|
+
import { EncryptedContentSymbol, unixNow } from "applesauce-core/helpers";
|
|
4
|
+
import { FakeUser } from "../../__tests__/fake-user.js";
|
|
5
|
+
import { WalletTokenBlueprint } from "../../blueprints/tokens.js";
|
|
6
|
+
import { decodeTokenFromEmojiString, dumbTokenSelection, encodeTokenToEmoji, unlockTokenContent } from "../tokens.js";
|
|
7
|
+
const user = new FakeUser();
|
|
8
|
+
const factory = new EventFactory({ signer: user });
|
|
9
|
+
describe("dumbTokenSelection", () => {
|
|
10
|
+
it("should select old tokens first", async () => {
|
|
11
|
+
const a = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
12
|
+
mint: "https://money.com",
|
|
13
|
+
proofs: [{ secret: "A", C: "A", id: "A", amount: 100 }],
|
|
14
|
+
}));
|
|
15
|
+
await unlockTokenContent(a, user);
|
|
16
|
+
const bDraft = await factory.create(WalletTokenBlueprint, {
|
|
17
|
+
mint: "https://money.com",
|
|
18
|
+
proofs: [{ secret: "B", C: "B", id: "B", amount: 50 }],
|
|
19
|
+
});
|
|
20
|
+
bDraft.created_at -= 60 * 60 * 7;
|
|
21
|
+
const b = await user.signEvent(bDraft);
|
|
22
|
+
await unlockTokenContent(b, user);
|
|
23
|
+
expect(dumbTokenSelection([a, b], 40).events).toEqual([b]);
|
|
24
|
+
});
|
|
25
|
+
it("should select enough tokens to total min amount", async () => {
|
|
26
|
+
const a = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
27
|
+
mint: "https://money.com",
|
|
28
|
+
proofs: [{ secret: "A", C: "A", id: "A", amount: 100 }],
|
|
29
|
+
}));
|
|
30
|
+
await unlockTokenContent(a, user);
|
|
31
|
+
const bDraft = await factory.create(WalletTokenBlueprint, {
|
|
32
|
+
mint: "https://money.com",
|
|
33
|
+
proofs: [{ secret: "B", C: "B", id: "B", amount: 50 }],
|
|
34
|
+
});
|
|
35
|
+
bDraft.created_at -= 60 * 60 * 7;
|
|
36
|
+
const b = await user.signEvent(bDraft);
|
|
37
|
+
await unlockTokenContent(b, user);
|
|
38
|
+
expect(dumbTokenSelection([a, b], 120).events).toEqual(expect.arrayContaining([a, b]));
|
|
39
|
+
});
|
|
40
|
+
it("should throw if not enough funds", async () => {
|
|
41
|
+
const a = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
42
|
+
mint: "https://money.com",
|
|
43
|
+
proofs: [{ secret: "A", C: "A", id: "A", amount: 100 }],
|
|
44
|
+
}));
|
|
45
|
+
await unlockTokenContent(a, user);
|
|
46
|
+
expect(() => dumbTokenSelection([a], 120)).toThrow();
|
|
47
|
+
});
|
|
48
|
+
it("should ignore locked tokens", async () => {
|
|
49
|
+
const a = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
50
|
+
mint: "https://money.com",
|
|
51
|
+
proofs: [{ secret: "A", C: "A", id: "A", amount: 100 }],
|
|
52
|
+
}));
|
|
53
|
+
await unlockTokenContent(a, user);
|
|
54
|
+
const bDraft = await factory.create(WalletTokenBlueprint, {
|
|
55
|
+
mint: "https://money.com",
|
|
56
|
+
proofs: [{ secret: "B", C: "B", id: "B", amount: 50 }],
|
|
57
|
+
});
|
|
58
|
+
bDraft.created_at -= 60 * 60 * 7;
|
|
59
|
+
const b = await user.signEvent(bDraft);
|
|
60
|
+
// manually remove the hidden content to lock it again
|
|
61
|
+
Reflect.deleteProperty(b, EncryptedContentSymbol);
|
|
62
|
+
expect(dumbTokenSelection([a, b], 20).events).toEqual([a]);
|
|
63
|
+
});
|
|
64
|
+
it("should ignore duplicate proofs", async () => {
|
|
65
|
+
const a = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
66
|
+
mint: "https://money.com",
|
|
67
|
+
proofs: [{ secret: "A", C: "A", id: "A", amount: 100 }],
|
|
68
|
+
}));
|
|
69
|
+
// create a second event with the same proofs
|
|
70
|
+
const b = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
71
|
+
mint: "https://money.com",
|
|
72
|
+
proofs: [{ secret: "A", C: "A", id: "A", amount: 100 }],
|
|
73
|
+
}));
|
|
74
|
+
expect(() => dumbTokenSelection([a, b], 150)).toThrow();
|
|
75
|
+
});
|
|
76
|
+
it("should include duplicate token events and ignore duplicate proofs", async () => {
|
|
77
|
+
const A = { secret: "A", C: "A", id: "A", amount: 100 };
|
|
78
|
+
const a = await user.signEvent({
|
|
79
|
+
...(await factory.create(WalletTokenBlueprint, {
|
|
80
|
+
mint: "https://money.com",
|
|
81
|
+
proofs: [A],
|
|
82
|
+
})),
|
|
83
|
+
// make event older
|
|
84
|
+
created_at: unixNow() - 100,
|
|
85
|
+
});
|
|
86
|
+
// create a second event with the same proofs
|
|
87
|
+
const a2 = await user.signEvent({
|
|
88
|
+
...(await factory.create(WalletTokenBlueprint, {
|
|
89
|
+
mint: "https://money.com",
|
|
90
|
+
proofs: [A],
|
|
91
|
+
})),
|
|
92
|
+
// make event older
|
|
93
|
+
created_at: a.created_at - 200,
|
|
94
|
+
});
|
|
95
|
+
const B = { secret: "B", C: "B", id: "B", amount: 50 };
|
|
96
|
+
const b = await user.signEvent(await factory.create(WalletTokenBlueprint, {
|
|
97
|
+
mint: "https://money.com",
|
|
98
|
+
proofs: [B],
|
|
99
|
+
}));
|
|
100
|
+
const result = dumbTokenSelection([a, a2, b], 150);
|
|
101
|
+
expect(result.events.map((e) => e.id)).toEqual(expect.arrayContaining([a.id, a2.id, b.id]));
|
|
102
|
+
expect(result.proofs).toEqual([A, B]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe("encodeTokenToEmoji", () => {
|
|
106
|
+
it("should encode token into emoji string", () => {
|
|
107
|
+
const token = "cashuBo2FteBtodHRwczovL3Rlc3RudXQuY2FzaHUuc3BhY2VhdWNzYXRhdIGiYWlIAJofKTJT5B5hcIGkYWEBYXN4QDdlZDBkMzk3NGQ5ZWM2OTc2YTAzYmZmYjdkMTA4NzIzZTBiMDRjMzRhNDc3MjlmNjMwOGJlODc3OTA2NTY0NDVhY1ghA36iYyOHCe4CnTxzORbcXFVeAbkMUFE6FqPWInujnAOcYWSjYWVYIJmHRwCQ0Uopkd3P5xb0MdcWQEaZz9hXWtcn-FMhZj8LYXNYIF4X9ybXxg5Pp0KSowfu4y_Aovo9iy3TXlLSaKyVJzz2YXJYIC_UFkoC5U9BpSgBTGUQgsjfz_emv5xykDiavZUfRN8E";
|
|
108
|
+
expect(encodeTokenToEmoji(token).length).toBeGreaterThan(token.length);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
const emoji = "π₯σ
σ
σ
£σ
σ
₯σ ²σ
σ ’σ Άσ
€σ
σ ²σ
€σ
σ
σ Έσ
σ
§σ
σ
ͺσ
σ
¦σ Όσ £σ
σ
σ
σ £σ
σ
₯σ
σ
σ
σ
₯σ
σ ’σ Άσ
ͺσ
σ Έσ
σ
₯σ
σ £σ ²σ
σ
σ ’σ
σ
σ
σ
σ Ύσ
ͺσ
σ
σ
σ
σ
σ Ήσ ·σ
σ
σ
σ
σ Ήσ ±σ Ίσ
σ
σ »σ
σ Ίσ
σ ₯σ ²σ ₯σ
σ
σ Ήσ ·σ
σ
σ
σ ΅σ ²σ
σ
σ Ύσ €σ
σ ΄σ
σ
σ
σ ΄σ ²σ
σ ½σ
ͺσ
σ £σ Ύσ ·σ
σ ₯σ
σ
σ ½σ ’σ Ώσ
σ
σ ’σ
σ
σ ±σ
ͺσ
σ
σ
σ
σ
σ
σ
σ
σ ½σ
σ ±σ €σ Ύσ
ͺσ Ήσ
ͺσ
σ
σ ²σ
σ ½σ ΄σ
σ
σ ½σ
ͺσ
σ
σ Ύσ ΄σ
σ £σ ½σ
σ
σ
σ Ύσ
σ ½σ
§σ Ώσ ·σ Ίσ
σ Ώσ ΄σ
σ £σ Ώσ
σ ±σ ’σ Ύσ
σ
σ σ Ύσ ΄σ
σ
σ
σ ‘σ
σ
σ ±σ £σ ¦σ
σ
σ
©σ Ώσ Έσ ³σ
σ €σ ³σ
σ
σ
¨σ
ͺσ Ώσ
σ
σ
σ
σ Άσ
σ
σ ±σ
σ
σ ½σ
σ Άσ ΅σ ¦σ Άσ
‘σ
σ
σ Ήσ
σ
₯σ
σ
σ ±σ Ώσ
σ
σ
σ
σ
σ
σ
σ
σ
σ Ήσ Ίσ
σ Έσ
σ
§σ ³σ
σ σ
σ
σ
σ
σ
σ £σ
σ ₯σ
¨σ
σ σ ½σ
σ
σ
σ
σ ΅σ
σ
σ
ͺσ ©σ
σ
σ
σ
€σ
σ
σ σ Άσ ½σ
σ
σ
σ ¨σ Όσ
σ
σ Ύσ
σ Ήσ Άσ €σ
σ ©σ
©σ
σ
σ
¨σ
σ ₯σ
σ
σ σ »σ
σ
σ
§σ
σ
₯σ €σ
©σ
σ ±σ
σ
¦σ
σ ©σ
σ
©σ £σ
σ
σ
σ Όσ
σ
σ »σ
©σ
σ Ίσ
ͺσ
ͺσ ’σ
σ
σ Ίσ
σ Ήσ ³σ
σ
σ Άσ
σ
σ ³σ ₯σ
σ ©σ ²σ
σ
σ
σ ²σ
σ ·σ
σ
σ
σ
£σ
σ
σ
ͺσ
σ
σ
σ
¦σ ₯σ
¨σ
©σ
σ ΄σ
σ
σ
¦σ
σ
σ
σ
σ Ύσ ¨σ ΅";
|
|
112
|
+
describe("decodeTokenFromEmojiString", () => {
|
|
113
|
+
it("should decode single emoji", () => {
|
|
114
|
+
expect(decodeTokenFromEmojiString(emoji)).toEqual(expect.objectContaining({
|
|
115
|
+
mint: "https://testnut.cashu.space",
|
|
116
|
+
proofs: [expect.any(Object)],
|
|
117
|
+
unit: "sat",
|
|
118
|
+
}));
|
|
119
|
+
});
|
|
120
|
+
it("should decode an emoji in text", () => {
|
|
121
|
+
expect(decodeTokenFromEmojiString("the money is in the emoji, " + emoji + " you can redeem it using cashu.me")).toEqual(expect.objectContaining({
|
|
122
|
+
mint: "https://testnut.cashu.space",
|
|
123
|
+
proofs: [expect.any(Object)],
|
|
124
|
+
unit: "sat",
|
|
125
|
+
}));
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"WalletBalanceModel",
|
|
8
|
+
"WalletHistoryModel",
|
|
9
|
+
"WalletModel",
|
|
10
|
+
"WalletRedeemedModel",
|
|
11
|
+
"WalletTokensModel",
|
|
12
|
+
]
|
|
13
|
+
`);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
2
|
+
import { EventStore } from "applesauce-core";
|
|
3
|
+
import { EventFactory } from "applesauce-factory";
|
|
4
|
+
import { generateSecretKey } from "nostr-tools";
|
|
5
|
+
import { beforeEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { FakeUser } from "../../__tests__/fake-user.js";
|
|
7
|
+
import { WalletBlueprint } from "../../blueprints/wallet.js";
|
|
8
|
+
import { lockWallet, unlockWallet } from "../../helpers/wallet.js";
|
|
9
|
+
import { WalletModel } from "../wallet.js";
|
|
10
|
+
const user = new FakeUser();
|
|
11
|
+
const factory = new EventFactory({ signer: user });
|
|
12
|
+
let eventStore;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
eventStore = new EventStore();
|
|
15
|
+
});
|
|
16
|
+
describe("WalletModel", () => {
|
|
17
|
+
it("it should update when event is unlocked", async () => {
|
|
18
|
+
const wallet = await user.signEvent(await factory.create(WalletBlueprint, [], generateSecretKey()));
|
|
19
|
+
lockWallet(wallet);
|
|
20
|
+
eventStore.add(wallet);
|
|
21
|
+
const spy = subscribeSpyTo(eventStore.model(WalletModel, await user.getPublicKey()));
|
|
22
|
+
await unlockWallet(wallet, user);
|
|
23
|
+
expect(spy.getValues()).toEqual([
|
|
24
|
+
expect.objectContaining({ locked: true }),
|
|
25
|
+
expect.objectContaining({ locked: false }),
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"EventOperations",
|
|
8
|
+
"TagOperations",
|
|
9
|
+
]
|
|
10
|
+
`);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -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,14 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import * as exports from "../index.js";
|
|
3
|
+
describe("exports", () => {
|
|
4
|
+
it("should export the expected functions", () => {
|
|
5
|
+
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"setHistoryContent",
|
|
8
|
+
"setHistoryRedeemed",
|
|
9
|
+
"setTokenContent",
|
|
10
|
+
"setWalletBackupContent",
|
|
11
|
+
]
|
|
12
|
+
`);
|
|
13
|
+
});
|
|
14
|
+
});
|