applesauce-wallet 0.0.0-next-20251209200210 → 0.0.0-next-20251220152312
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 +2 -2
- package/dist/actions/index.js +2 -2
- 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/wallet.d.ts +5 -1
- package/dist/blueprints/wallet.js +6 -3
- package/dist/casts/__register__.d.ts +20 -0
- package/dist/casts/__register__.js +41 -0
- package/dist/casts/index.d.ts +6 -0
- package/dist/casts/index.js +6 -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 +5 -1
- package/dist/helpers/index.js +5 -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/{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 +1 -1
- package/dist/operations/index.js +1 -1
- 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 +15 -6
- 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
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { blueprint } from "applesauce-core";
|
|
2
|
+
import { modifyHiddenTags } from "applesauce-core/operations";
|
|
2
3
|
import { WALLET_BACKUP_KIND, WALLET_KIND } from "../helpers/wallet.js";
|
|
3
|
-
import { setBackupContent,
|
|
4
|
+
import { setBackupContent, setMintTags, setPrivateKeyTag, setRelayTags } from "../operations/wallet.js";
|
|
4
5
|
/** A blueprint to create a new 17375 wallet */
|
|
5
|
-
export function WalletBlueprint(mints, privateKey) {
|
|
6
|
-
return blueprint(WALLET_KIND,
|
|
6
|
+
export function WalletBlueprint({ mints, privateKey, relays, }) {
|
|
7
|
+
return blueprint(WALLET_KIND,
|
|
8
|
+
// Use top level modifyHiddenTags to avoid multiple encryption operations
|
|
9
|
+
modifyHiddenTags(setMintTags(mints), privateKey ? setPrivateKeyTag(privateKey) : undefined, relays ? setRelayTags(relays) : undefined));
|
|
7
10
|
}
|
|
8
11
|
/** A blueprint that creates a new 375 wallet backup event */
|
|
9
12
|
export function WalletBackupBlueprint(wallet) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ChainableObservable } from "applesauce-common/observable";
|
|
2
|
+
import { NutzapInfo } from "./nutzap-info.js";
|
|
3
|
+
import { Nutzap } from "./nutzap.js";
|
|
4
|
+
import { Wallet } from "./wallet.js";
|
|
5
|
+
declare module "applesauce-common/casts/user" {
|
|
6
|
+
interface User {
|
|
7
|
+
readonly wallet$: ChainableObservable<Wallet | undefined>;
|
|
8
|
+
readonly nutzap$: ChainableObservable<NutzapInfo | undefined>;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
declare module "applesauce-common/casts/stream" {
|
|
12
|
+
interface Stream {
|
|
13
|
+
readonly nutzaps$: ChainableObservable<Nutzap[]>;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
declare module "applesauce-common/casts/note" {
|
|
17
|
+
interface Note {
|
|
18
|
+
nutzaps$(): ChainableObservable<Nutzap[]>;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Note, Stream } from "applesauce-common/casts";
|
|
2
|
+
import { User } from "applesauce-common/casts/user";
|
|
3
|
+
import { castEventStream, castTimelineStream } from "applesauce-common/observable";
|
|
4
|
+
import { buildCommonEventRelationFilters } from "applesauce-core/helpers";
|
|
5
|
+
import { switchMap } from "rxjs";
|
|
6
|
+
import { NUTZAP_KIND } from "../helpers/nutzap.js";
|
|
7
|
+
import { WALLET_KIND } from "../helpers/wallet.js";
|
|
8
|
+
import { NUTZAP_INFO_KIND } from "../helpers/nutzap-info.js";
|
|
9
|
+
import { NutzapInfo } from "./nutzap-info.js";
|
|
10
|
+
import { Nutzap } from "./nutzap.js";
|
|
11
|
+
import { Wallet } from "./wallet.js";
|
|
12
|
+
Object.defineProperty(User.prototype, "wallet$", {
|
|
13
|
+
get: function () {
|
|
14
|
+
return this.$$ref("wallet$", (store) => this.outboxes$.pipe(switchMap((outboxes) => store
|
|
15
|
+
.replaceable({ kind: WALLET_KIND, pubkey: this.pubkey, relays: outboxes })
|
|
16
|
+
.pipe(castEventStream(Wallet, store)))));
|
|
17
|
+
},
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: false,
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(User.prototype, "nutzap$", {
|
|
22
|
+
get: function () {
|
|
23
|
+
return this.$$ref("nutzap$", (store) => store.replaceable(NUTZAP_INFO_KIND, this.pubkey).pipe(castEventStream(NutzapInfo, store)));
|
|
24
|
+
},
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: false,
|
|
27
|
+
});
|
|
28
|
+
Note.prototype.nutzaps$ = function () {
|
|
29
|
+
return this.$$ref("nutzaps$", (store) => store
|
|
30
|
+
.timeline(buildCommonEventRelationFilters({ kinds: [NUTZAP_KIND] }, this.event))
|
|
31
|
+
.pipe(castTimelineStream(Nutzap, store)));
|
|
32
|
+
};
|
|
33
|
+
Object.defineProperty(Stream.prototype, "nutzaps$", {
|
|
34
|
+
get: function () {
|
|
35
|
+
return this.$$ref("nutzaps$", (store) => store
|
|
36
|
+
.timeline(buildCommonEventRelationFilters({ kinds: [NUTZAP_KIND] }, this.event))
|
|
37
|
+
.pipe(castTimelineStream(Nutzap, store)));
|
|
38
|
+
},
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: false,
|
|
41
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CastRefEventStore, EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
|
+
import { NutzapInfoEvent } from "../helpers/nutzap-info.js";
|
|
4
|
+
export declare class NutzapInfo extends EventCast<NutzapInfoEvent> {
|
|
5
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
6
|
+
get relays(): string[];
|
|
7
|
+
get mints(): {
|
|
8
|
+
mint: string;
|
|
9
|
+
units?: string[];
|
|
10
|
+
}[];
|
|
11
|
+
/** The p2pk public key to use when zapping */
|
|
12
|
+
get publicKey(): string | undefined;
|
|
13
|
+
get p2pk(): string | undefined;
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { getNutzapInfoMints, getNutzapInfoPubkey, getNutzapInfoRelays, isValidNutzapInfo, } from "../helpers/nutzap-info.js";
|
|
3
|
+
export class NutzapInfo extends EventCast {
|
|
4
|
+
constructor(event, store) {
|
|
5
|
+
if (!isValidNutzapInfo(event))
|
|
6
|
+
throw new Error("Invalid nutzap info");
|
|
7
|
+
super(event, store);
|
|
8
|
+
}
|
|
9
|
+
get relays() {
|
|
10
|
+
return getNutzapInfoRelays(this.event);
|
|
11
|
+
}
|
|
12
|
+
get mints() {
|
|
13
|
+
return getNutzapInfoMints(this.event);
|
|
14
|
+
}
|
|
15
|
+
/** The p2pk public key to use when zapping */
|
|
16
|
+
get publicKey() {
|
|
17
|
+
return getNutzapInfoPubkey(this.event);
|
|
18
|
+
}
|
|
19
|
+
get p2pk() {
|
|
20
|
+
return this.publicKey;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CastRefEventStore, EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
|
+
import { NutzapEvent } from "../helpers/nutzap.js";
|
|
4
|
+
export declare class Nutzap extends EventCast<NutzapEvent> {
|
|
5
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
6
|
+
get proofs(): import("@cashu/cashu-ts").Proof[];
|
|
7
|
+
get mint(): string;
|
|
8
|
+
get amount(): number;
|
|
9
|
+
get recipient(): import("applesauce-common/casts").User;
|
|
10
|
+
get sender(): import("applesauce-common/casts").User;
|
|
11
|
+
get comment(): string | undefined;
|
|
12
|
+
/** The pointer to the event that was zapped */
|
|
13
|
+
get pointer(): import("nostr-tools/nip19").EventPointer | import("nostr-tools/nip19").AddressPointer | undefined;
|
|
14
|
+
/** The event that was zapped */
|
|
15
|
+
get zapped$(): import("applesauce-common/observable").ChainableObservable<import("nostr-tools").Event | undefined>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { castUser } from "applesauce-common/casts/user";
|
|
3
|
+
import { of } from "rxjs";
|
|
4
|
+
import { getNutzapAddressPointer, getNutzapAmount, getNutzapComment, getNutzapEventPointer, getNutzapMint, getNutzapProofs, getNutzapRecipient, isValidNutzap, } from "../helpers/nutzap.js";
|
|
5
|
+
export class Nutzap extends EventCast {
|
|
6
|
+
constructor(event, store) {
|
|
7
|
+
if (!isValidNutzap(event))
|
|
8
|
+
throw new Error("Invalid nutzap");
|
|
9
|
+
super(event, store);
|
|
10
|
+
}
|
|
11
|
+
get proofs() {
|
|
12
|
+
return getNutzapProofs(this.event);
|
|
13
|
+
}
|
|
14
|
+
get mint() {
|
|
15
|
+
return getNutzapMint(this.event);
|
|
16
|
+
}
|
|
17
|
+
get amount() {
|
|
18
|
+
return getNutzapAmount(this.event);
|
|
19
|
+
}
|
|
20
|
+
get recipient() {
|
|
21
|
+
return castUser(getNutzapRecipient(this.event), this.store);
|
|
22
|
+
}
|
|
23
|
+
get sender() {
|
|
24
|
+
return this.author;
|
|
25
|
+
}
|
|
26
|
+
get comment() {
|
|
27
|
+
return getNutzapComment(this.event);
|
|
28
|
+
}
|
|
29
|
+
/** The pointer to the event that was zapped */
|
|
30
|
+
get pointer() {
|
|
31
|
+
return getNutzapAddressPointer(this.event) ?? getNutzapEventPointer(this.event);
|
|
32
|
+
}
|
|
33
|
+
/** The event that was zapped */
|
|
34
|
+
get zapped$() {
|
|
35
|
+
return this.$$ref("zapped$", (store) => (this.pointer ? store.event(this.pointer) : of(undefined)));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CastRefEventStore, EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { HiddenContentSigner } from "applesauce-core/helpers";
|
|
3
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
4
|
+
import { WalletHistoryEvent } from "../helpers/history.js";
|
|
5
|
+
export declare class WalletHistory extends EventCast<WalletHistoryEvent> {
|
|
6
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
7
|
+
/** The embedded history metadata */
|
|
8
|
+
get meta(): import("../helpers/history.js").HistoryContent | undefined;
|
|
9
|
+
get direction(): import("../helpers/history.js").HistoryDirection | undefined;
|
|
10
|
+
get amount(): number | undefined;
|
|
11
|
+
get redeemed(): string[];
|
|
12
|
+
get unlocked(): boolean;
|
|
13
|
+
unlock(signer: HiddenContentSigner): Promise<import("../helpers/history.js").HistoryContent>;
|
|
14
|
+
get meta$(): import("applesauce-common/observable").ChainableObservable<import("../helpers/history.js").HistoryContent | undefined>;
|
|
15
|
+
get amount$(): import("applesauce-common/observable").ChainableObservable<number | undefined>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { watchEventUpdates } from "applesauce-core/observable";
|
|
3
|
+
import { of } from "rxjs";
|
|
4
|
+
import { map } from "rxjs/operators";
|
|
5
|
+
import { getHistoryContent, getHistoryRedeemed, isHistoryContentUnlocked, isValidWalletHistory, unlockHistoryContent, } from "../helpers/history.js";
|
|
6
|
+
export class WalletHistory extends EventCast {
|
|
7
|
+
constructor(event, store) {
|
|
8
|
+
if (!isValidWalletHistory(event))
|
|
9
|
+
throw new Error("Invalid wallet history");
|
|
10
|
+
super(event, store);
|
|
11
|
+
}
|
|
12
|
+
/** The embedded history metadata */
|
|
13
|
+
get meta() {
|
|
14
|
+
return getHistoryContent(this.event);
|
|
15
|
+
}
|
|
16
|
+
// Direct getters (return undefined if locked)
|
|
17
|
+
get direction() {
|
|
18
|
+
return this.meta?.direction;
|
|
19
|
+
}
|
|
20
|
+
get amount() {
|
|
21
|
+
return this.meta?.amount;
|
|
22
|
+
}
|
|
23
|
+
get redeemed() {
|
|
24
|
+
return getHistoryRedeemed(this.event);
|
|
25
|
+
}
|
|
26
|
+
// Unlocking pattern
|
|
27
|
+
get unlocked() {
|
|
28
|
+
return isHistoryContentUnlocked(this.event);
|
|
29
|
+
}
|
|
30
|
+
async unlock(signer) {
|
|
31
|
+
return unlockHistoryContent(this.event, signer);
|
|
32
|
+
}
|
|
33
|
+
// Observable that emits when history is unlocked
|
|
34
|
+
get meta$() {
|
|
35
|
+
return this.$$ref("meta$", (store) => of(this.event).pipe(watchEventUpdates(store), map((event) => event && getHistoryContent(event))));
|
|
36
|
+
}
|
|
37
|
+
get amount$() {
|
|
38
|
+
return this.$$ref("amount$", () => this.meta$.pipe(map((meta) => meta?.amount)));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CastRefEventStore, EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { HiddenContentSigner } from "applesauce-core/helpers";
|
|
3
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
4
|
+
import { WalletTokenEvent } from "../helpers/tokens.js";
|
|
5
|
+
export declare class WalletToken extends EventCast<WalletTokenEvent> {
|
|
6
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
7
|
+
get meta(): import("../helpers/tokens.js").TokenContent | undefined;
|
|
8
|
+
get proofs(): {
|
|
9
|
+
amount: number;
|
|
10
|
+
secret: string;
|
|
11
|
+
C: string;
|
|
12
|
+
id: string;
|
|
13
|
+
}[] | undefined;
|
|
14
|
+
get mint(): string | undefined;
|
|
15
|
+
get amount(): number | undefined;
|
|
16
|
+
get deleted(): string[] | undefined;
|
|
17
|
+
get unlocked(): boolean;
|
|
18
|
+
unlock(signer: HiddenContentSigner): Promise<import("../helpers/tokens.js").TokenContent>;
|
|
19
|
+
get meta$(): import("applesauce-common/observable").ChainableObservable<import("../helpers/tokens.js").TokenContent>;
|
|
20
|
+
get mint$(): import("applesauce-common/observable").ChainableObservable<string>;
|
|
21
|
+
get proofs$(): import("applesauce-common/observable").ChainableObservable<{
|
|
22
|
+
amount: number;
|
|
23
|
+
secret: string;
|
|
24
|
+
C: string;
|
|
25
|
+
id: string;
|
|
26
|
+
}[]>;
|
|
27
|
+
get amount$(): import("applesauce-common/observable").ChainableObservable<number>;
|
|
28
|
+
get deleted$(): import("applesauce-common/observable").ChainableObservable<string[]>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { sumProofs } from "@cashu/cashu-ts";
|
|
2
|
+
import { EventCast } from "applesauce-common/casts";
|
|
3
|
+
import { defined, watchEventUpdates } from "applesauce-core/observable";
|
|
4
|
+
import { of } from "rxjs";
|
|
5
|
+
import { map } from "rxjs/operators";
|
|
6
|
+
import { getTokenContent, isTokenContentUnlocked, isValidWalletToken, unlockTokenContent, } from "../helpers/tokens.js";
|
|
7
|
+
export class WalletToken extends EventCast {
|
|
8
|
+
constructor(event, store) {
|
|
9
|
+
if (!isValidWalletToken(event))
|
|
10
|
+
throw new Error("Invalid wallet token");
|
|
11
|
+
super(event, store);
|
|
12
|
+
}
|
|
13
|
+
get meta() {
|
|
14
|
+
return getTokenContent(this.event);
|
|
15
|
+
}
|
|
16
|
+
// Direct getters (return undefined if locked)
|
|
17
|
+
get proofs() {
|
|
18
|
+
return this.meta?.proofs;
|
|
19
|
+
}
|
|
20
|
+
get mint() {
|
|
21
|
+
return this.meta?.mint;
|
|
22
|
+
}
|
|
23
|
+
get amount() {
|
|
24
|
+
return this.proofs && sumProofs(this.proofs);
|
|
25
|
+
}
|
|
26
|
+
get deleted() {
|
|
27
|
+
return this.meta?.del;
|
|
28
|
+
}
|
|
29
|
+
// Unlocking pattern
|
|
30
|
+
get unlocked() {
|
|
31
|
+
return isTokenContentUnlocked(this.event);
|
|
32
|
+
}
|
|
33
|
+
async unlock(signer) {
|
|
34
|
+
return unlockTokenContent(this.event, signer);
|
|
35
|
+
}
|
|
36
|
+
// Observable that emits when token is unlocked
|
|
37
|
+
get meta$() {
|
|
38
|
+
return this.$$ref("meta$", (store) => of(this.event).pipe(watchEventUpdates(store), map((event) => event && getTokenContent(event)), defined()));
|
|
39
|
+
}
|
|
40
|
+
get mint$() {
|
|
41
|
+
return this.$$ref("mint$", () => this.meta$.pipe(map((meta) => meta?.mint)));
|
|
42
|
+
}
|
|
43
|
+
get proofs$() {
|
|
44
|
+
return this.$$ref("proofs$", () => this.meta$.pipe(map((meta) => meta?.proofs)));
|
|
45
|
+
}
|
|
46
|
+
get amount$() {
|
|
47
|
+
return this.$$ref("amount$", () => this.proofs$.pipe(map(sumProofs)));
|
|
48
|
+
}
|
|
49
|
+
get deleted$() {
|
|
50
|
+
return this.$$ref("deleted$", () => this.meta$.pipe(map((meta) => meta?.del)));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CastRefEventStore, EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { HiddenContentSigner } from "applesauce-core/helpers";
|
|
3
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
4
|
+
import { WalletEvent } from "../helpers/wallet.js";
|
|
5
|
+
import { WalletHistory } from "./wallet-history.js";
|
|
6
|
+
import { WalletToken } from "./wallet-token.js";
|
|
7
|
+
export declare class Wallet extends EventCast<WalletEvent> {
|
|
8
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
9
|
+
get mints(): string[];
|
|
10
|
+
get relays(): string[] | undefined;
|
|
11
|
+
get privateKey(): Uint8Array<ArrayBufferLike> | null | undefined;
|
|
12
|
+
get unlocked(): boolean;
|
|
13
|
+
unlock(signer: HiddenContentSigner): Promise<{
|
|
14
|
+
mints: string[];
|
|
15
|
+
privateKey: Uint8Array | null;
|
|
16
|
+
relays: string[];
|
|
17
|
+
}>;
|
|
18
|
+
get mints$(): import("applesauce-common/observable").ChainableObservable<string[] | undefined>;
|
|
19
|
+
get relays$(): import("applesauce-common/observable").ChainableObservable<string[] | undefined>;
|
|
20
|
+
get balance$(): import("applesauce-common/observable").ChainableObservable<Record<string, number>>;
|
|
21
|
+
/** The p2pk locking private key for this wallet */
|
|
22
|
+
get privateKey$(): import("applesauce-common/observable").ChainableObservable<Uint8Array<ArrayBufferLike> | null | undefined>;
|
|
23
|
+
get tokens$(): import("applesauce-common/observable").ChainableObservable<WalletToken[]>;
|
|
24
|
+
get history$(): import("applesauce-common/observable").ChainableObservable<WalletHistory[]>;
|
|
25
|
+
get backups$(): import("applesauce-common/observable").ChainableObservable<import("nostr-tools").Event[]>;
|
|
26
|
+
get received$(): import("applesauce-common/observable").ChainableObservable<string[]>;
|
|
27
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EventCast } from "applesauce-common/casts";
|
|
2
|
+
import { castTimelineStream } from "applesauce-common/observable";
|
|
3
|
+
import { watchEventUpdates } from "applesauce-core/observable";
|
|
4
|
+
import { of } from "rxjs";
|
|
5
|
+
import { map } from "rxjs/operators";
|
|
6
|
+
import { getWalletMints, getWalletPrivateKey, getWalletRelays, isValidWallet, isWalletUnlocked, unlockWallet, WALLET_BACKUP_KIND, } from "../helpers/wallet.js";
|
|
7
|
+
import { WalletHistoryModel } from "../models/history.js";
|
|
8
|
+
import { WalletBalanceModel, WalletTokensModel } from "../models/tokens.js";
|
|
9
|
+
import { WalletHistory } from "./wallet-history.js";
|
|
10
|
+
import { WalletToken } from "./wallet-token.js";
|
|
11
|
+
import { ReceivedNutzapsModel } from "../models/nutzap.js";
|
|
12
|
+
export class Wallet extends EventCast {
|
|
13
|
+
constructor(event, store) {
|
|
14
|
+
if (!isValidWallet(event))
|
|
15
|
+
throw new Error("Invalid wallet event");
|
|
16
|
+
super(event, store);
|
|
17
|
+
}
|
|
18
|
+
// Direct getters (return undefined if locked)
|
|
19
|
+
get mints() {
|
|
20
|
+
return getWalletMints(this.event);
|
|
21
|
+
}
|
|
22
|
+
get relays() {
|
|
23
|
+
return getWalletRelays(this.event);
|
|
24
|
+
}
|
|
25
|
+
get privateKey() {
|
|
26
|
+
return getWalletPrivateKey(this.event);
|
|
27
|
+
}
|
|
28
|
+
// Unlocking pattern
|
|
29
|
+
get unlocked() {
|
|
30
|
+
return isWalletUnlocked(this.event);
|
|
31
|
+
}
|
|
32
|
+
async unlock(signer) {
|
|
33
|
+
return unlockWallet(this.event, signer);
|
|
34
|
+
}
|
|
35
|
+
// Observable that emits when wallet is unlocked
|
|
36
|
+
get mints$() {
|
|
37
|
+
return this.$$ref("mints$", (store) => of(this.event).pipe(watchEventUpdates(store), map((event) => event && getWalletMints(event))));
|
|
38
|
+
}
|
|
39
|
+
get relays$() {
|
|
40
|
+
return this.$$ref("relays$", (store) => of(this.event).pipe(watchEventUpdates(store), map((event) => event && getWalletRelays(event))));
|
|
41
|
+
}
|
|
42
|
+
get balance$() {
|
|
43
|
+
return this.$$ref("balance$", (store) => store.model(WalletBalanceModel, this.event.pubkey));
|
|
44
|
+
}
|
|
45
|
+
/** The p2pk locking private key for this wallet */
|
|
46
|
+
get privateKey$() {
|
|
47
|
+
return this.$$ref("privateKey$", (store) => of(this.event).pipe(watchEventUpdates(store), map((event) => event && getWalletPrivateKey(event))));
|
|
48
|
+
}
|
|
49
|
+
// Observables for related events
|
|
50
|
+
get tokens$() {
|
|
51
|
+
return this.$$ref("tokens$", (store) => store.model(WalletTokensModel, this.event.pubkey).pipe(castTimelineStream(WalletToken, store)));
|
|
52
|
+
}
|
|
53
|
+
get history$() {
|
|
54
|
+
return this.$$ref("history$", (store) => store.model(WalletHistoryModel, this.event.pubkey).pipe(castTimelineStream(WalletHistory, store)));
|
|
55
|
+
}
|
|
56
|
+
get backups$() {
|
|
57
|
+
return this.$$ref("backups$", (store) => store.timeline({ kinds: [WALLET_BACKUP_KIND], authors: [this.event.pubkey] }));
|
|
58
|
+
}
|
|
59
|
+
get received$() {
|
|
60
|
+
return this.$$ref("received$", (store) => store.model(ReceivedNutzapsModel, this.event.pubkey));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Proof, Token } from "@cashu/cashu-ts";
|
|
2
|
+
/** Internal method for creating a unique id for each proof */
|
|
3
|
+
export declare function getProofUID(proof: Proof): string;
|
|
4
|
+
/**
|
|
5
|
+
* Extracts the P2PK locking pubkey from a proof's secret
|
|
6
|
+
* @param proof the cashu proof to extract the pubkey from
|
|
7
|
+
* @returns the pubkey, or undefined if not P2PK locked
|
|
8
|
+
*/
|
|
9
|
+
export declare function getProofP2PKPubkey(proof: Proof): string | undefined;
|
|
10
|
+
/** Internal method to filter out duplicate proofs */
|
|
11
|
+
export declare function ignoreDuplicateProofs(seen?: Set<string>): (proof: Proof) => boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Returns a decoded cashu token inside an unicode emoji
|
|
14
|
+
* @see https://github.com/cashubtc/cashu.me/blob/1194a7b9ee2f43305e38304de7bef8839601ff4d/src/components/ReceiveTokenDialog.vue#L387
|
|
15
|
+
*/
|
|
16
|
+
export declare function decodeTokenFromEmojiString(str: string): Token | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Encodes a token into an emoji char
|
|
19
|
+
* @see https://github.com/cashubtc/cashu.me/blob/1194a7b9ee2f43305e38304de7bef8839601ff4d/src/components/SendTokenDialog.vue#L710
|
|
20
|
+
*/
|
|
21
|
+
export declare function encodeTokenToEmoji(token: Token | string, emoji?: string): string;
|
|
@@ -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,9 @@ 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";
|
package/dist/helpers/index.js
CHANGED
|
@@ -2,5 +2,9 @@ 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";
|