applesauce-wallet 0.0.0-next-20251209200210 → 0.0.0-next-20251231055351
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/actions/common.d.ts +4 -0
- package/dist/actions/common.js +15 -0
- package/dist/actions/index.d.ts +3 -2
- package/dist/actions/index.js +3 -2
- package/dist/actions/mint-recomendation.d.ts +30 -0
- package/dist/actions/mint-recomendation.js +96 -0
- package/dist/actions/{zap-info.d.ts → nutzap-info.d.ts} +20 -3
- package/dist/actions/nutzap-info.js +117 -0
- package/dist/actions/nutzaps.d.ts +24 -0
- package/dist/actions/nutzaps.js +154 -0
- package/dist/actions/tokens.d.ts +77 -7
- package/dist/actions/tokens.js +332 -69
- package/dist/actions/wallet.d.ts +18 -3
- package/dist/actions/wallet.js +74 -32
- package/dist/blueprints/history.d.ts +1 -1
- package/dist/blueprints/history.js +1 -1
- package/dist/blueprints/index.d.ts +1 -0
- package/dist/blueprints/index.js +1 -0
- package/dist/blueprints/mint-recommendation.d.ts +16 -0
- package/dist/blueprints/mint-recommendation.js +11 -0
- package/dist/blueprints/wallet.d.ts +5 -1
- package/dist/blueprints/wallet.js +6 -3
- package/dist/casts/__register__.d.ts +22 -0
- package/dist/casts/__register__.js +52 -0
- package/dist/casts/index.d.ts +8 -0
- package/dist/casts/index.js +8 -0
- package/dist/casts/mint-info.d.ts +18 -0
- package/dist/casts/mint-info.js +42 -0
- package/dist/casts/mint-recommendation.d.ts +16 -0
- package/dist/casts/mint-recommendation.js +29 -0
- package/dist/casts/nutzap-info.d.ts +14 -0
- package/dist/casts/nutzap-info.js +22 -0
- package/dist/casts/nutzap.d.ts +16 -0
- package/dist/casts/nutzap.js +37 -0
- package/dist/casts/wallet-history.d.ts +16 -0
- package/dist/casts/wallet-history.js +40 -0
- package/dist/casts/wallet-token.d.ts +29 -0
- package/dist/casts/wallet-token.js +52 -0
- package/dist/casts/wallet.d.ts +27 -0
- package/dist/casts/wallet.js +62 -0
- package/dist/helpers/cashu.d.ts +21 -0
- package/dist/helpers/cashu.js +105 -0
- package/dist/helpers/couch.d.ts +11 -0
- package/dist/helpers/couch.js +1 -0
- package/dist/helpers/history.d.ts +5 -1
- package/dist/helpers/history.js +13 -4
- package/dist/helpers/index.d.ts +7 -1
- package/dist/helpers/index.js +7 -1
- package/dist/helpers/indexed-db-couch.d.ts +34 -0
- package/dist/helpers/indexed-db-couch.js +119 -0
- package/dist/helpers/local-storage-couch.d.ts +29 -0
- package/dist/helpers/local-storage-couch.js +78 -0
- package/dist/helpers/mint-info.d.ts +40 -0
- package/dist/helpers/mint-info.js +80 -0
- package/dist/helpers/mint-recommendation.d.ts +41 -0
- package/dist/helpers/mint-recommendation.js +54 -0
- package/dist/helpers/{zap-info.d.ts → nutzap-info.d.ts} +10 -1
- package/dist/helpers/{zap-info.js → nutzap-info.js} +22 -10
- package/dist/helpers/nutzap.d.ts +15 -0
- package/dist/helpers/nutzap.js +57 -3
- package/dist/helpers/tokens.d.ts +9 -18
- package/dist/helpers/tokens.js +64 -94
- package/dist/helpers/wallet.d.ts +16 -6
- package/dist/helpers/wallet.js +40 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/history.d.ts +1 -1
- package/dist/models/history.js +7 -10
- package/dist/models/index.d.ts +0 -1
- package/dist/models/index.js +0 -1
- package/dist/models/nutzap.d.ts +2 -0
- package/dist/models/nutzap.js +8 -0
- package/dist/models/tokens.d.ts +2 -2
- package/dist/models/tokens.js +14 -17
- package/dist/operations/history.js +1 -1
- package/dist/operations/index.d.ts +2 -1
- package/dist/operations/index.js +2 -1
- package/dist/operations/mint-recommendation.d.ts +13 -0
- package/dist/operations/mint-recommendation.js +26 -0
- package/dist/operations/nutzap-info.d.ts +21 -0
- package/dist/operations/nutzap-info.js +71 -0
- package/dist/operations/wallet.d.ts +10 -1
- package/dist/operations/wallet.js +33 -3
- package/package.json +37 -28
- package/dist/actions/zap-info.js +0 -83
- package/dist/actions/zaps.d.ts +0 -8
- package/dist/actions/zaps.js +0 -30
- package/dist/models/wallet.d.ts +0 -13
- package/dist/models/wallet.js +0 -21
- package/dist/operations/zap-info.d.ts +0 -10
- package/dist/operations/zap-info.js +0 -17
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { getDecodedToken, getEncodedToken } from "@cashu/cashu-ts";
|
|
2
|
+
import { safeParse } from "applesauce-core/helpers";
|
|
3
|
+
/** Internal method for creating a unique id for each proof */
|
|
4
|
+
export function getProofUID(proof) {
|
|
5
|
+
return proof.id + proof.amount + proof.C + proof.secret;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Extracts the P2PK locking pubkey from a proof's secret
|
|
9
|
+
* @param proof the cashu proof to extract the pubkey from
|
|
10
|
+
* @returns the pubkey, or undefined if not P2PK locked
|
|
11
|
+
*/
|
|
12
|
+
export function getProofP2PKPubkey(proof) {
|
|
13
|
+
const secret = safeParse(proof.secret);
|
|
14
|
+
if (!secret)
|
|
15
|
+
return;
|
|
16
|
+
if (!Array.isArray(secret))
|
|
17
|
+
return;
|
|
18
|
+
if (secret[0] !== "P2PK")
|
|
19
|
+
return;
|
|
20
|
+
const proofPubkey = secret[1]?.data;
|
|
21
|
+
if (!proofPubkey || typeof proofPubkey !== "string")
|
|
22
|
+
return;
|
|
23
|
+
return proofPubkey;
|
|
24
|
+
}
|
|
25
|
+
/** Internal method to filter out duplicate proofs */
|
|
26
|
+
export function ignoreDuplicateProofs(seen = new Set()) {
|
|
27
|
+
return (proof) => {
|
|
28
|
+
const id = getProofUID(proof);
|
|
29
|
+
if (seen.has(id))
|
|
30
|
+
return false;
|
|
31
|
+
else {
|
|
32
|
+
seen.add(id);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Returns a decoded cashu token inside an unicode emoji
|
|
39
|
+
* @see https://github.com/cashubtc/cashu.me/blob/1194a7b9ee2f43305e38304de7bef8839601ff4d/src/components/ReceiveTokenDialog.vue#L387
|
|
40
|
+
*/
|
|
41
|
+
export function decodeTokenFromEmojiString(str) {
|
|
42
|
+
try {
|
|
43
|
+
let decoded = [];
|
|
44
|
+
const chars = Array.from(str);
|
|
45
|
+
if (!chars.length)
|
|
46
|
+
return undefined;
|
|
47
|
+
const fromVariationSelector = function (char) {
|
|
48
|
+
const codePoint = char.codePointAt(0);
|
|
49
|
+
if (codePoint === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
// Handle Variation Selectors (VS1-VS16): U+FE00 to U+FE0F
|
|
52
|
+
if (codePoint >= 0xfe00 && codePoint <= 0xfe0f) {
|
|
53
|
+
// Maps FE00->0, FE01->1, ..., FE0F->15
|
|
54
|
+
const byteValue = codePoint - 0xfe00;
|
|
55
|
+
return String.fromCharCode(byteValue);
|
|
56
|
+
}
|
|
57
|
+
// Handle Variation Selectors Supplement (VS17-VS256): U+E0100 to U+E01EF
|
|
58
|
+
if (codePoint >= 0xe0100 && codePoint <= 0xe01ef) {
|
|
59
|
+
// Maps E0100->16, E0101->17, ..., E01EF->255
|
|
60
|
+
const byteValue = codePoint - 0xe0100 + 16;
|
|
61
|
+
return String.fromCharCode(byteValue);
|
|
62
|
+
}
|
|
63
|
+
// No Variation Selector
|
|
64
|
+
return null;
|
|
65
|
+
};
|
|
66
|
+
// Check all input chars for peanut data
|
|
67
|
+
for (const char of chars) {
|
|
68
|
+
let byte = fromVariationSelector(char);
|
|
69
|
+
if (byte === null && decoded.length > 0) {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
else if (byte === null) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
decoded.push(byte); // got some
|
|
76
|
+
}
|
|
77
|
+
// Switch out token if we found peanut data
|
|
78
|
+
let decodedString = decoded.join("");
|
|
79
|
+
return getDecodedToken(decodedString);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Encodes a token into an emoji char
|
|
87
|
+
* @see https://github.com/cashubtc/cashu.me/blob/1194a7b9ee2f43305e38304de7bef8839601ff4d/src/components/SendTokenDialog.vue#L710
|
|
88
|
+
*/
|
|
89
|
+
export function encodeTokenToEmoji(token, emoji = "🥜") {
|
|
90
|
+
return (emoji +
|
|
91
|
+
Array.from(typeof token === "string" ? token : getEncodedToken(token))
|
|
92
|
+
.map((char) => {
|
|
93
|
+
const byteValue = char.charCodeAt(0);
|
|
94
|
+
// For byte values 0-15, use Variation Selectors (VS1-VS16): U+FE00 to U+FE0F
|
|
95
|
+
if (byteValue >= 0 && byteValue <= 15) {
|
|
96
|
+
return String.fromCodePoint(0xfe00 + byteValue);
|
|
97
|
+
}
|
|
98
|
+
// For byte values 16-255, use Variation Selectors Supplement (VS17-VS256): U+E0100 to U+E01EF
|
|
99
|
+
if (byteValue >= 16 && byteValue <= 255) {
|
|
100
|
+
return String.fromCodePoint(0xe0100 + (byteValue - 16));
|
|
101
|
+
}
|
|
102
|
+
return "";
|
|
103
|
+
})
|
|
104
|
+
.join(""));
|
|
105
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
type ClearMethod = () => void | Promise<void>;
|
|
3
|
+
export interface Couch {
|
|
4
|
+
/** Store a token in the couch */
|
|
5
|
+
store(token: Token): ClearMethod | Promise<ClearMethod>;
|
|
6
|
+
/** Clear all tokens from the couch */
|
|
7
|
+
clear(): void | Promise<void>;
|
|
8
|
+
/** Get all tokens currently stored in the couch */
|
|
9
|
+
getAll(): Token[] | Promise<Token[]>;
|
|
10
|
+
}
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { HiddenContentSigner, UnlockedHiddenTags } from "applesauce-core/helpers";
|
|
2
|
-
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
2
|
+
import { KnownEvent, NostrEvent } from "applesauce-core/helpers/event";
|
|
3
3
|
export declare const WALLET_HISTORY_KIND = 7376;
|
|
4
|
+
/** Validated wallet history event */
|
|
5
|
+
export type WalletHistoryEvent = KnownEvent<typeof WALLET_HISTORY_KIND>;
|
|
6
|
+
/** Checks if an event is a valid wallet history event */
|
|
7
|
+
export declare function isValidWalletHistory(event: NostrEvent): event is WalletHistoryEvent;
|
|
4
8
|
export type HistoryDirection = "in" | "out";
|
|
5
9
|
export type HistoryContent = {
|
|
6
10
|
/** The direction of the transaction, in = received, out = sent */
|
package/dist/helpers/history.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { getHiddenTags, isETag, isHiddenTagsUnlocked, lockHiddenTags, notifyEventUpdate, setHiddenTagsEncryptionMethod, unlockHiddenTags, } from "applesauce-core/helpers";
|
|
1
|
+
import { getHiddenTags, hasHiddenTags, isETag, isHiddenTagsUnlocked, lockHiddenTags, notifyEventUpdate, setHiddenTagsEncryptionMethod, unlockHiddenTags, } from "applesauce-core/helpers";
|
|
2
2
|
export const WALLET_HISTORY_KIND = 7376;
|
|
3
|
+
/** Checks if an event is a valid wallet history event */
|
|
4
|
+
export function isValidWalletHistory(event) {
|
|
5
|
+
return event.kind === WALLET_HISTORY_KIND && hasHiddenTags(event);
|
|
6
|
+
}
|
|
3
7
|
// Enable hidden content for wallet history kind
|
|
4
8
|
setHiddenTagsEncryptionMethod(WALLET_HISTORY_KIND, "nip44");
|
|
5
9
|
export const HistoryContentSymbol = Symbol.for("history-content");
|
|
@@ -9,11 +13,16 @@ export function getHistoryRedeemed(history) {
|
|
|
9
13
|
}
|
|
10
14
|
/** Checks if the history contents are locked */
|
|
11
15
|
export function isHistoryContentUnlocked(history) {
|
|
12
|
-
|
|
16
|
+
// Wrap in try catch to avoid throwing validation errors
|
|
17
|
+
try {
|
|
18
|
+
return (HistoryContentSymbol in history || (isHiddenTagsUnlocked(history) && getHistoryContent(history) !== undefined));
|
|
19
|
+
}
|
|
20
|
+
catch { }
|
|
21
|
+
return false;
|
|
13
22
|
}
|
|
14
23
|
export function getHistoryContent(history) {
|
|
15
24
|
// Return cached value if it exists
|
|
16
|
-
if (
|
|
25
|
+
if (HistoryContentSymbol in history)
|
|
17
26
|
return history[HistoryContentSymbol];
|
|
18
27
|
// Get hidden tags
|
|
19
28
|
const tags = getHiddenTags(history);
|
|
@@ -41,7 +50,7 @@ export function getHistoryContent(history) {
|
|
|
41
50
|
/** Decrypts a wallet history event */
|
|
42
51
|
export async function unlockHistoryContent(history, signer) {
|
|
43
52
|
// Return cached value if it exists
|
|
44
|
-
if (
|
|
53
|
+
if (HistoryContentSymbol in history)
|
|
45
54
|
return history[HistoryContentSymbol];
|
|
46
55
|
// Unlock hidden tags if needed
|
|
47
56
|
await unlockHiddenTags(history, signer);
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -2,5 +2,11 @@ export * from "./animated-qr.js";
|
|
|
2
2
|
export * from "./history.js";
|
|
3
3
|
export * from "./tokens.js";
|
|
4
4
|
export * from "./wallet.js";
|
|
5
|
-
export * from "./
|
|
5
|
+
export * from "./nutzap-info.js";
|
|
6
6
|
export * from "./nutzap.js";
|
|
7
|
+
export * from "./cashu.js";
|
|
8
|
+
export * from "./couch.js";
|
|
9
|
+
export * from "./local-storage-couch.js";
|
|
10
|
+
export * from "./indexed-db-couch.js";
|
|
11
|
+
export * from "./mint-info.js";
|
|
12
|
+
export * from "./mint-recommendation.js";
|
package/dist/helpers/index.js
CHANGED
|
@@ -2,5 +2,11 @@ export * from "./animated-qr.js";
|
|
|
2
2
|
export * from "./history.js";
|
|
3
3
|
export * from "./tokens.js";
|
|
4
4
|
export * from "./wallet.js";
|
|
5
|
-
export * from "./
|
|
5
|
+
export * from "./nutzap-info.js";
|
|
6
6
|
export * from "./nutzap.js";
|
|
7
|
+
export * from "./cashu.js";
|
|
8
|
+
export * from "./couch.js";
|
|
9
|
+
export * from "./local-storage-couch.js";
|
|
10
|
+
export * from "./indexed-db-couch.js";
|
|
11
|
+
export * from "./mint-info.js";
|
|
12
|
+
export * from "./mint-recommendation.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
import type { Couch } from "./couch.js";
|
|
3
|
+
/**
|
|
4
|
+
* A simple IndexedDB-based implementation of the Couch interface.
|
|
5
|
+
* Stores tokens in the browser's IndexedDB for better performance with larger datasets.
|
|
6
|
+
*/
|
|
7
|
+
export declare class IndexedDBCouch implements Couch {
|
|
8
|
+
private dbName;
|
|
9
|
+
private storeName;
|
|
10
|
+
private db;
|
|
11
|
+
private initPromise;
|
|
12
|
+
constructor(dbName?: string, storeName?: string);
|
|
13
|
+
/**
|
|
14
|
+
* Generate a unique ID for each stored token.
|
|
15
|
+
*/
|
|
16
|
+
private generateId;
|
|
17
|
+
/**
|
|
18
|
+
* Initialize the IndexedDB database and object store.
|
|
19
|
+
*/
|
|
20
|
+
private init;
|
|
21
|
+
/**
|
|
22
|
+
* Store a token in the couch.
|
|
23
|
+
* Returns a function that can be called to remove this specific token.
|
|
24
|
+
*/
|
|
25
|
+
store(token: Token): Promise<() => Promise<void>>;
|
|
26
|
+
/**
|
|
27
|
+
* Clear all tokens from the couch.
|
|
28
|
+
*/
|
|
29
|
+
clear(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Get all tokens currently stored in the couch.
|
|
32
|
+
*/
|
|
33
|
+
getAll(): Promise<Token[]>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { getEncodedToken, getDecodedToken } from "@cashu/cashu-ts";
|
|
2
|
+
const DB_NAME = "applesauce-wallet-couch";
|
|
3
|
+
const STORE_NAME = "tokens";
|
|
4
|
+
const DB_VERSION = 1;
|
|
5
|
+
/**
|
|
6
|
+
* A simple IndexedDB-based implementation of the Couch interface.
|
|
7
|
+
* Stores tokens in the browser's IndexedDB for better performance with larger datasets.
|
|
8
|
+
*/
|
|
9
|
+
export class IndexedDBCouch {
|
|
10
|
+
dbName;
|
|
11
|
+
storeName;
|
|
12
|
+
db = null;
|
|
13
|
+
initPromise = null;
|
|
14
|
+
constructor(dbName = DB_NAME, storeName = STORE_NAME) {
|
|
15
|
+
this.dbName = dbName;
|
|
16
|
+
this.storeName = storeName;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate a unique ID for each stored token.
|
|
20
|
+
*/
|
|
21
|
+
generateId() {
|
|
22
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Initialize the IndexedDB database and object store.
|
|
26
|
+
*/
|
|
27
|
+
async init() {
|
|
28
|
+
if (this.db)
|
|
29
|
+
return this.db;
|
|
30
|
+
if (this.initPromise)
|
|
31
|
+
return this.initPromise;
|
|
32
|
+
this.initPromise = new Promise((resolve, reject) => {
|
|
33
|
+
if (typeof window === "undefined" || !window.indexedDB) {
|
|
34
|
+
reject(new Error("IndexedDB is not available"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const request = window.indexedDB.open(this.dbName, DB_VERSION);
|
|
38
|
+
request.onerror = () => reject(request.error);
|
|
39
|
+
request.onsuccess = () => {
|
|
40
|
+
this.db = request.result;
|
|
41
|
+
resolve(this.db);
|
|
42
|
+
};
|
|
43
|
+
request.onupgradeneeded = (event) => {
|
|
44
|
+
const db = event.target.result;
|
|
45
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
46
|
+
db.createObjectStore(this.storeName, { keyPath: "id" });
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
return this.initPromise;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Store a token in the couch.
|
|
54
|
+
* Returns a function that can be called to remove this specific token.
|
|
55
|
+
*/
|
|
56
|
+
async store(token) {
|
|
57
|
+
const db = await this.init();
|
|
58
|
+
const id = this.generateId();
|
|
59
|
+
const encodedToken = getEncodedToken(token);
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const transaction = db.transaction([this.storeName], "readwrite");
|
|
62
|
+
const store = transaction.objectStore(this.storeName);
|
|
63
|
+
const request = store.add({ id, encodedToken });
|
|
64
|
+
request.onsuccess = () => {
|
|
65
|
+
// Return a function to remove this specific token by ID
|
|
66
|
+
resolve(async () => {
|
|
67
|
+
const removeDb = await this.init();
|
|
68
|
+
const removeTransaction = removeDb.transaction([this.storeName], "readwrite");
|
|
69
|
+
const removeStore = removeTransaction.objectStore(this.storeName);
|
|
70
|
+
const removeRequest = removeStore.delete(id);
|
|
71
|
+
await new Promise((resolve, reject) => {
|
|
72
|
+
removeRequest.onsuccess = () => resolve();
|
|
73
|
+
removeRequest.onerror = () => reject(removeRequest.error);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
request.onerror = () => reject(request.error);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Clear all tokens from the couch.
|
|
82
|
+
*/
|
|
83
|
+
async clear() {
|
|
84
|
+
const db = await this.init();
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
const transaction = db.transaction([this.storeName], "readwrite");
|
|
87
|
+
const store = transaction.objectStore(this.storeName);
|
|
88
|
+
const request = store.clear();
|
|
89
|
+
request.onsuccess = () => resolve();
|
|
90
|
+
request.onerror = () => reject(request.error);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get all tokens currently stored in the couch.
|
|
95
|
+
*/
|
|
96
|
+
async getAll() {
|
|
97
|
+
const db = await this.init();
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
const transaction = db.transaction([this.storeName], "readonly");
|
|
100
|
+
const store = transaction.objectStore(this.storeName);
|
|
101
|
+
const request = store.getAll();
|
|
102
|
+
request.onsuccess = () => {
|
|
103
|
+
const results = request.result;
|
|
104
|
+
const tokens = results
|
|
105
|
+
.map((item) => {
|
|
106
|
+
try {
|
|
107
|
+
return getDecodedToken(item.encodedToken);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.filter((token) => token !== null);
|
|
114
|
+
resolve(tokens);
|
|
115
|
+
};
|
|
116
|
+
request.onerror = () => reject(request.error);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
import type { Couch } from "./couch.js";
|
|
3
|
+
/**
|
|
4
|
+
* A simple localStorage-based implementation of the Couch interface.
|
|
5
|
+
* Stores tokens in the browser's localStorage.
|
|
6
|
+
*/
|
|
7
|
+
export declare class LocalStorageCouch implements Couch {
|
|
8
|
+
private storageKey;
|
|
9
|
+
constructor(storageKey?: string);
|
|
10
|
+
/**
|
|
11
|
+
* Generate a unique ID for each stored token.
|
|
12
|
+
*/
|
|
13
|
+
private generateId;
|
|
14
|
+
/**
|
|
15
|
+
* Store a token in the couch.
|
|
16
|
+
* Returns a function that can be called to remove this specific token.
|
|
17
|
+
*/
|
|
18
|
+
store(token: Token): () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Clear all tokens from the couch.
|
|
21
|
+
*/
|
|
22
|
+
clear(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get all tokens currently stored in the couch.
|
|
25
|
+
*/
|
|
26
|
+
getAll(): Token[];
|
|
27
|
+
private getStoredTokens;
|
|
28
|
+
private saveStoredTokens;
|
|
29
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { getEncodedToken, getDecodedToken } from "@cashu/cashu-ts";
|
|
2
|
+
const STORAGE_KEY = "applesauce:couch:tokens";
|
|
3
|
+
/**
|
|
4
|
+
* A simple localStorage-based implementation of the Couch interface.
|
|
5
|
+
* Stores tokens in the browser's localStorage.
|
|
6
|
+
*/
|
|
7
|
+
export class LocalStorageCouch {
|
|
8
|
+
storageKey;
|
|
9
|
+
constructor(storageKey = STORAGE_KEY) {
|
|
10
|
+
this.storageKey = storageKey;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Generate a unique ID for each stored token.
|
|
14
|
+
*/
|
|
15
|
+
generateId() {
|
|
16
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Store a token in the couch.
|
|
20
|
+
* Returns a function that can be called to remove this specific token.
|
|
21
|
+
*/
|
|
22
|
+
store(token) {
|
|
23
|
+
const id = this.generateId();
|
|
24
|
+
const encodedToken = getEncodedToken(token);
|
|
25
|
+
const stored = this.getStoredTokens();
|
|
26
|
+
stored.push({ id, encodedToken });
|
|
27
|
+
this.saveStoredTokens(stored);
|
|
28
|
+
// Return a function to remove this specific token by ID
|
|
29
|
+
return () => {
|
|
30
|
+
const currentStored = this.getStoredTokens();
|
|
31
|
+
const filtered = currentStored.filter((item) => item.id !== id);
|
|
32
|
+
this.saveStoredTokens(filtered);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Clear all tokens from the couch.
|
|
37
|
+
*/
|
|
38
|
+
clear() {
|
|
39
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
40
|
+
window.localStorage.removeItem(this.storageKey);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get all tokens currently stored in the couch.
|
|
45
|
+
*/
|
|
46
|
+
getAll() {
|
|
47
|
+
const stored = this.getStoredTokens();
|
|
48
|
+
return stored
|
|
49
|
+
.map((item) => {
|
|
50
|
+
try {
|
|
51
|
+
return getDecodedToken(item.encodedToken);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
.filter((token) => token !== null);
|
|
58
|
+
}
|
|
59
|
+
getStoredTokens() {
|
|
60
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const stored = window.localStorage.getItem(this.storageKey);
|
|
65
|
+
if (!stored)
|
|
66
|
+
return [];
|
|
67
|
+
return JSON.parse(stored);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
saveStoredTokens(tokens) {
|
|
74
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
75
|
+
window.localStorage.setItem(this.storageKey, JSON.stringify(tokens));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { KnownEvent } from "applesauce-core/helpers";
|
|
2
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
|
+
export declare const CASHU_MINT_INFO_KIND = 38172;
|
|
4
|
+
export type CashuMintInfoEvent = KnownEvent<typeof CASHU_MINT_INFO_KIND>;
|
|
5
|
+
export type NetworkType = "mainnet" | "testnet" | "signet" | "regtest";
|
|
6
|
+
export declare const CashuMintPubkeySymbol: unique symbol;
|
|
7
|
+
export declare const CashuMintURLSymbol: unique symbol;
|
|
8
|
+
export declare const CashuMintNutsSymbol: unique symbol;
|
|
9
|
+
export declare const CashuMintNetworkSymbol: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the mint's pubkey from a kind:38172 cashu mint info event
|
|
12
|
+
* This is found in the `d` tag and is the pubkey from the mint's `/v1/info` endpoint
|
|
13
|
+
*/
|
|
14
|
+
export declare function getCashuMintPubkey(event: NostrEvent): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the mint URL from a kind:38172 cashu mint info event
|
|
17
|
+
* This is the URL to the cashu mint (from the `u` tag)
|
|
18
|
+
*/
|
|
19
|
+
export declare function getCashuMintURL(event: CashuMintInfoEvent): string;
|
|
20
|
+
export declare function getCashuMintURL(event: NostrEvent): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the supported nuts from a kind:38172 cashu mint info event
|
|
23
|
+
* This is a comma-separated list of nut numbers (e.g., "1,2,3,4,5,6,7")
|
|
24
|
+
*/
|
|
25
|
+
export declare function getCashuMintNuts(event: NostrEvent): number[];
|
|
26
|
+
/**
|
|
27
|
+
* Returns the network type from a kind:38172 cashu mint info event
|
|
28
|
+
* Should be one of: mainnet, testnet, signet, or regtest
|
|
29
|
+
*/
|
|
30
|
+
export declare function getCashuMintNetwork(event: NostrEvent): NetworkType | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Returns the optional metadata content from a kind:38172 cashu mint info event
|
|
33
|
+
* This is a kind:0-style metadata JSON object, useful when the pubkey is not a normal user
|
|
34
|
+
*/
|
|
35
|
+
export declare function getCashuMintMetadata(event: NostrEvent): Record<string, any> | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Validates that an event is a proper kind:38172 cashu mint info event
|
|
38
|
+
* Checks that the event is kind 38172 and has the required `d` and `u` tags
|
|
39
|
+
*/
|
|
40
|
+
export declare function isValidCashuMintInfo(event?: NostrEvent): event is CashuMintInfoEvent;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getOrComputeCachedValue, getTagValue, isHex, safeParse } from "applesauce-core/helpers";
|
|
2
|
+
import { getReplaceableIdentifier } from "applesauce-core/helpers/event";
|
|
3
|
+
// NIP-87 Cashu Mint Information Event Kind
|
|
4
|
+
export const CASHU_MINT_INFO_KIND = 38172;
|
|
5
|
+
// Symbols for caching computed values
|
|
6
|
+
export const CashuMintPubkeySymbol = Symbol.for("cashu-mint-pubkey");
|
|
7
|
+
export const CashuMintURLSymbol = Symbol.for("cashu-mint-url");
|
|
8
|
+
export const CashuMintNutsSymbol = Symbol.for("cashu-mint-nuts");
|
|
9
|
+
export const CashuMintNetworkSymbol = Symbol.for("cashu-mint-network");
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Cashu Mint Information Event (kind:38172) Helpers
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Returns the mint's pubkey from a kind:38172 cashu mint info event
|
|
15
|
+
* This is found in the `d` tag and is the pubkey from the mint's `/v1/info` endpoint
|
|
16
|
+
*/
|
|
17
|
+
export function getCashuMintPubkey(event) {
|
|
18
|
+
return getOrComputeCachedValue(event, CashuMintPubkeySymbol, () => {
|
|
19
|
+
const identifier = getReplaceableIdentifier(event);
|
|
20
|
+
// Only return hex pubkeys
|
|
21
|
+
if (!isHex(identifier))
|
|
22
|
+
return undefined;
|
|
23
|
+
return identifier || undefined;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function getCashuMintURL(event) {
|
|
27
|
+
return getOrComputeCachedValue(event, CashuMintURLSymbol, () => {
|
|
28
|
+
const url = getTagValue(event, "u");
|
|
29
|
+
return url && URL.canParse(url) ? url : undefined;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns the supported nuts from a kind:38172 cashu mint info event
|
|
34
|
+
* This is a comma-separated list of nut numbers (e.g., "1,2,3,4,5,6,7")
|
|
35
|
+
*/
|
|
36
|
+
export function getCashuMintNuts(event) {
|
|
37
|
+
return getOrComputeCachedValue(event, CashuMintNutsSymbol, () => {
|
|
38
|
+
const nutsStr = getTagValue(event, "nuts");
|
|
39
|
+
if (!nutsStr)
|
|
40
|
+
return [];
|
|
41
|
+
return nutsStr
|
|
42
|
+
.split(",")
|
|
43
|
+
.map((n) => parseInt(n.trim(), 10))
|
|
44
|
+
.filter((n) => !isNaN(n));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns the network type from a kind:38172 cashu mint info event
|
|
49
|
+
* Should be one of: mainnet, testnet, signet, or regtest
|
|
50
|
+
*/
|
|
51
|
+
export function getCashuMintNetwork(event) {
|
|
52
|
+
return getOrComputeCachedValue(event, CashuMintNetworkSymbol, () => {
|
|
53
|
+
const network = getTagValue(event, "n");
|
|
54
|
+
if (!network)
|
|
55
|
+
return undefined;
|
|
56
|
+
const validNetworks = ["mainnet", "testnet", "signet", "regtest"];
|
|
57
|
+
return validNetworks.includes(network) ? network : undefined;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Returns the optional metadata content from a kind:38172 cashu mint info event
|
|
62
|
+
* This is a kind:0-style metadata JSON object, useful when the pubkey is not a normal user
|
|
63
|
+
*/
|
|
64
|
+
export function getCashuMintMetadata(event) {
|
|
65
|
+
if (!event.content)
|
|
66
|
+
return undefined;
|
|
67
|
+
return safeParse(event.content);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Validates that an event is a proper kind:38172 cashu mint info event
|
|
71
|
+
* Checks that the event is kind 38172 and has the required `d` and `u` tags
|
|
72
|
+
*/
|
|
73
|
+
export function isValidCashuMintInfo(event) {
|
|
74
|
+
if (!event)
|
|
75
|
+
return false;
|
|
76
|
+
if (event.kind !== CASHU_MINT_INFO_KIND)
|
|
77
|
+
return false;
|
|
78
|
+
const url = getCashuMintURL(event);
|
|
79
|
+
return url !== undefined;
|
|
80
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { KnownEvent } from "applesauce-core/helpers";
|
|
2
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
|
+
import { AddressPointer } from "applesauce-core/helpers/pointers";
|
|
4
|
+
export declare const MINT_RECOMMENDATION_KIND = 38000;
|
|
5
|
+
export type MintRecommendationEvent = KnownEvent<typeof MINT_RECOMMENDATION_KIND>;
|
|
6
|
+
export declare const RecommendationKindSymbol: unique symbol;
|
|
7
|
+
export declare const RecommendationURLSymbol: unique symbol;
|
|
8
|
+
export declare const RecommendationAddressPointerSymbol: unique symbol;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the recommended event kind from a kind:38000 recommendation event
|
|
11
|
+
* This should be 38172 for cashu mints
|
|
12
|
+
*/
|
|
13
|
+
export declare function getRecommendationKind(event: MintRecommendationEvent): number;
|
|
14
|
+
export declare function getRecommendationKind(event: NostrEvent): number | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Returns the d-identifier from a kind:38000 recommendation event
|
|
17
|
+
* This is the kind:38172 event identifier this event is recommending
|
|
18
|
+
*/
|
|
19
|
+
export declare function getRecommendationMintPubkey(event: NostrEvent): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the URL from a kind:38000 recommendation event
|
|
22
|
+
* This is an optional `u` tag that provides a recommended way to connect to the cashu mint
|
|
23
|
+
* Each recommendation event recommends a single mint, so there is at most one `u` tag
|
|
24
|
+
*/
|
|
25
|
+
export declare function getRecommendationURL(event: MintRecommendationEvent): string | undefined;
|
|
26
|
+
export declare function getRecommendationURL(event: NostrEvent): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Returns the address pointer from a kind:38000 recommendation event
|
|
29
|
+
* This `a` tag points to the kind:38172 event of the cashu mint
|
|
30
|
+
* The first value is the event identifier, the second value is a relay hint
|
|
31
|
+
* Each recommendation event recommends a single mint, so there is at most one `a` tag
|
|
32
|
+
*/
|
|
33
|
+
export declare function getRecommendationAddressPointer(event: MintRecommendationEvent): AddressPointer | undefined;
|
|
34
|
+
export declare function getRecommendationAddressPointer(event: NostrEvent): AddressPointer | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Validates that an event is a proper kind:38000 recommendation event
|
|
37
|
+
* Checks that the event is kind 38000, has the required `k` and `d` tags,
|
|
38
|
+
* and that the `k` tag is 38172 (cashu mint kind)
|
|
39
|
+
* Each recommendation event recommends a single cashu mint
|
|
40
|
+
*/
|
|
41
|
+
export declare function isValidMintRecommendation(event?: NostrEvent): event is MintRecommendationEvent;
|