applesauce-wallet 5.0.0 → 6.0.0
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/mint-recomendation.js +15 -16
- package/dist/actions/nutzap-info.js +38 -46
- package/dist/actions/nutzaps.js +21 -12
- package/dist/actions/tokens.js +51 -39
- package/dist/actions/wallet.js +18 -25
- package/dist/casts/__register__.d.ts +1 -1
- package/dist/casts/__register__.js +1 -1
- package/dist/casts/mint-info.d.ts +1 -0
- package/dist/casts/mint-info.js +3 -1
- package/dist/casts/mint-recommendation.d.ts +1 -0
- package/dist/casts/mint-recommendation.js +1 -0
- package/dist/casts/nutzap-info.d.ts +1 -0
- package/dist/casts/nutzap-info.js +1 -0
- package/dist/casts/nutzap.d.ts +4 -3
- package/dist/casts/nutzap.js +1 -0
- package/dist/casts/wallet-history.d.ts +3 -2
- package/dist/casts/wallet-history.js +1 -0
- package/dist/casts/wallet-token.d.ts +6 -5
- package/dist/casts/wallet-token.js +1 -0
- package/dist/casts/wallet.d.ts +1 -0
- package/dist/casts/wallet.js +5 -1
- package/dist/factories/history.d.ts +10 -0
- package/dist/factories/history.js +16 -0
- package/dist/{blueprints → factories}/index.d.ts +4 -3
- package/dist/{blueprints → factories}/index.js +4 -3
- package/dist/factories/mint-recommendation.d.ts +14 -0
- package/dist/factories/mint-recommendation.js +29 -0
- package/dist/factories/nutzap-info.d.ts +31 -0
- package/dist/factories/nutzap-info.js +44 -0
- package/dist/factories/nutzap.d.ts +15 -0
- package/dist/factories/nutzap.js +35 -0
- package/dist/factories/tokens.d.ts +9 -0
- package/dist/factories/tokens.js +13 -0
- package/dist/factories/wallet.d.ts +17 -0
- package/dist/factories/wallet.js +40 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/operations/history.d.ts +2 -2
- package/dist/operations/history.js +2 -2
- package/dist/operations/mint-recommendation.js +1 -1
- package/dist/operations/nutzap.js +1 -1
- package/dist/operations/tokens.d.ts +1 -1
- package/dist/operations/tokens.js +5 -5
- package/dist/operations/wallet.d.ts +12 -8
- package/dist/operations/wallet.js +18 -14
- package/package.json +13 -13
- package/dist/blueprints/history.d.ts +0 -4
- package/dist/blueprints/history.js +0 -11
- package/dist/blueprints/mint-recommendation.d.ts +0 -16
- package/dist/blueprints/mint-recommendation.js +0 -11
- package/dist/blueprints/tokens.d.ts +0 -7
- package/dist/blueprints/tokens.js +0 -11
- package/dist/blueprints/wallet.d.ts +0 -9
- package/dist/blueprints/wallet.js +0 -14
- package/dist/blueprints/zaps.d.ts +0 -8
- package/dist/blueprints/zaps.js +0 -12
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Wallet } from "@cashu/cashu-ts";
|
|
2
|
-
import {
|
|
2
|
+
import { MintRecommendationFactory } from "../factories/mint-recommendation.js";
|
|
3
3
|
import { getCashuMintPubkey, getCashuMintURL, isValidCashuMintInfo } from "../helpers/mint-info.js";
|
|
4
4
|
import { MINT_RECOMMENDATION_KIND, getRecommendationMintPubkey, isValidMintRecommendation, } from "../helpers/mint-recommendation.js";
|
|
5
|
-
import { setComment } from "../operations/mint-recommendation.js";
|
|
6
5
|
// Helper function to fetch mint pubkey from URL
|
|
7
6
|
async function fetchMintPubkey(mintUrl) {
|
|
8
7
|
// Fetch mint info directly from the /v1/info endpoint
|
|
@@ -21,10 +20,12 @@ import "../casts/__register__.js";
|
|
|
21
20
|
* Can accept a mint URL, Wallet instance, or MintInfo event.
|
|
22
21
|
*/
|
|
23
22
|
export function RecommendMint(input, options) {
|
|
24
|
-
return async ({
|
|
23
|
+
return async ({ signer, publish }) => {
|
|
24
|
+
if (!signer)
|
|
25
|
+
throw new Error("Missing signer");
|
|
25
26
|
let mintPubkey;
|
|
26
27
|
let url;
|
|
27
|
-
let
|
|
28
|
+
let mintInfoEvent = undefined;
|
|
28
29
|
// Handle different input types
|
|
29
30
|
if (typeof input === "string") {
|
|
30
31
|
if (!URL.canParse(input))
|
|
@@ -43,18 +44,16 @@ export function RecommendMint(input, options) {
|
|
|
43
44
|
throw new Error("Invalid mint info event. Expected kind:38172 with required `d` and `u` tags.");
|
|
44
45
|
mintPubkey = getCashuMintPubkey(input);
|
|
45
46
|
url = getCashuMintURL(input);
|
|
46
|
-
// Use the event itself as address pointer (
|
|
47
|
-
|
|
47
|
+
// Use the event itself as address pointer (factory will convert it)
|
|
48
|
+
mintInfoEvent = input;
|
|
48
49
|
}
|
|
49
50
|
// Create the mint recommendation event
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
})
|
|
57
|
-
.then(sign);
|
|
51
|
+
let recommendationFactory = MintRecommendationFactory.create(url || "", mintPubkey);
|
|
52
|
+
if (mintInfoEvent)
|
|
53
|
+
recommendationFactory = recommendationFactory.mintInfo(mintInfoEvent);
|
|
54
|
+
if (options?.comment)
|
|
55
|
+
recommendationFactory = recommendationFactory.comment(options.comment);
|
|
56
|
+
const recommendation = await recommendationFactory.sign(signer);
|
|
58
57
|
// Publish the event
|
|
59
58
|
await publish(recommendation, options?.relays);
|
|
60
59
|
};
|
|
@@ -64,7 +63,7 @@ export function RecommendMint(input, options) {
|
|
|
64
63
|
* Can accept a mint pubkey (to find the recommendation) or the recommendation event directly.
|
|
65
64
|
*/
|
|
66
65
|
export function UpdateMintRecommendation(input, comment, options) {
|
|
67
|
-
return async ({ events,
|
|
66
|
+
return async ({ events, signer, self, publish }) => {
|
|
68
67
|
let recommendation;
|
|
69
68
|
// Handle different input types
|
|
70
69
|
if (typeof input === "string") {
|
|
@@ -89,7 +88,7 @@ export function UpdateMintRecommendation(input, comment, options) {
|
|
|
89
88
|
recommendation = input;
|
|
90
89
|
}
|
|
91
90
|
// Update the comment
|
|
92
|
-
const updated = await
|
|
91
|
+
const updated = await MintRecommendationFactory.modify(recommendation).comment(comment).sign(signer);
|
|
93
92
|
// Publish the updated event
|
|
94
93
|
await publish(updated, options?.relays);
|
|
95
94
|
};
|
|
@@ -1,74 +1,70 @@
|
|
|
1
1
|
import { getNutzapInfoRelays, NUTZAP_INFO_KIND } from "../helpers/nutzap-info.js";
|
|
2
|
-
import {
|
|
2
|
+
import { NutzapInfoFactory } from "../factories/nutzap-info.js";
|
|
3
3
|
// Make sure the nutzap$ is registered on the user class
|
|
4
4
|
import "../casts/__register__.js";
|
|
5
5
|
/** An action to add a relay to the kind 10019 nutzap info event */
|
|
6
6
|
export function AddNutzapInfoRelay(relay) {
|
|
7
|
-
return async ({ events,
|
|
7
|
+
return async ({ events, signer, self, publish }) => {
|
|
8
8
|
if (typeof relay === "string")
|
|
9
9
|
relay = [relay];
|
|
10
|
-
const operations = relay.map((r) => addNutzapInfoRelay(r));
|
|
11
10
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
await publish(signed, relays);
|
|
11
|
+
let factory = nutzapInfo ? NutzapInfoFactory.modify(nutzapInfo) : NutzapInfoFactory.create();
|
|
12
|
+
for (const r of relay)
|
|
13
|
+
factory = factory.addRelay(r);
|
|
14
|
+
const signed = await factory.sign(signer);
|
|
15
|
+
await publish(signed, getNutzapInfoRelays(signed));
|
|
18
16
|
};
|
|
19
17
|
}
|
|
20
18
|
/** An action to remove a relay from the kind 10019 nutzap info event */
|
|
21
19
|
export function RemoveNutzapInfoRelay(relay) {
|
|
22
|
-
return async ({ events,
|
|
20
|
+
return async ({ events, signer, self, publish }) => {
|
|
23
21
|
if (typeof relay === "string")
|
|
24
22
|
relay = [relay];
|
|
25
23
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
26
24
|
if (!nutzapInfo)
|
|
27
25
|
return;
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
await publish(signed,
|
|
26
|
+
let factory = NutzapInfoFactory.modify(nutzapInfo);
|
|
27
|
+
for (const r of relay)
|
|
28
|
+
factory = factory.removeRelay(r);
|
|
29
|
+
const signed = await factory.sign(signer);
|
|
30
|
+
await publish(signed, getNutzapInfoRelays(signed));
|
|
33
31
|
};
|
|
34
32
|
}
|
|
35
33
|
/** An action to add a mint to the kind 10019 nutzap info event */
|
|
36
34
|
export function AddNutzapInfoMint(mint) {
|
|
37
|
-
return async ({ events,
|
|
35
|
+
return async ({ events, signer, self, publish }) => {
|
|
38
36
|
const mints = Array.isArray(mint) ? mint : [mint];
|
|
39
|
-
const operations = mints.map((m) => addNutzapInfoMint(m));
|
|
40
37
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
await publish(signed, relays);
|
|
38
|
+
let factory = nutzapInfo ? NutzapInfoFactory.modify(nutzapInfo) : NutzapInfoFactory.create();
|
|
39
|
+
for (const m of mints)
|
|
40
|
+
factory = factory.addMint(m);
|
|
41
|
+
const signed = await factory.sign(signer);
|
|
42
|
+
await publish(signed, getNutzapInfoRelays(signed));
|
|
47
43
|
};
|
|
48
44
|
}
|
|
49
45
|
/** An action to remove a mint from the kind 10019 nutzap info event */
|
|
50
46
|
export function RemoveNutzapInfoMint(mint) {
|
|
51
|
-
return async ({ events,
|
|
47
|
+
return async ({ events, signer, self, publish }) => {
|
|
52
48
|
if (typeof mint === "string")
|
|
53
49
|
mint = [mint];
|
|
54
50
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
55
51
|
if (!nutzapInfo)
|
|
56
52
|
return;
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
await publish(signed,
|
|
53
|
+
let factory = NutzapInfoFactory.modify(nutzapInfo);
|
|
54
|
+
for (const m of mint)
|
|
55
|
+
factory = factory.removeMint(m);
|
|
56
|
+
const signed = await factory.sign(signer);
|
|
57
|
+
await publish(signed, getNutzapInfoRelays(signed));
|
|
62
58
|
};
|
|
63
59
|
}
|
|
64
60
|
/** An action to update the entire nutzap info event */
|
|
65
61
|
export function UpdateNutzapInfo(relays, mints) {
|
|
66
|
-
return async ({ events,
|
|
67
|
-
const operations = [setNutzapInfoRelays(relays), setNutzapInfoMints(mints)];
|
|
62
|
+
return async ({ events, signer, self, publish }) => {
|
|
68
63
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
69
|
-
const signed = nutzapInfo
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
const signed = await (nutzapInfo ? NutzapInfoFactory.modify(nutzapInfo) : NutzapInfoFactory.create())
|
|
65
|
+
.setRelays(relays)
|
|
66
|
+
.setMints(mints)
|
|
67
|
+
.sign(signer);
|
|
72
68
|
await publish(signed, relays);
|
|
73
69
|
};
|
|
74
70
|
}
|
|
@@ -77,14 +73,12 @@ export function UpdateNutzapInfo(relays, mints) {
|
|
|
77
73
|
* @throws if the nutzap info does not exist
|
|
78
74
|
*/
|
|
79
75
|
export function SetNutzapInfoMints(mints) {
|
|
80
|
-
return async ({ events, self,
|
|
76
|
+
return async ({ events, self, signer, publish }) => {
|
|
81
77
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
82
78
|
if (!nutzapInfo)
|
|
83
79
|
throw new Error("Nutzap info does not exist");
|
|
84
|
-
const signed = await
|
|
85
|
-
|
|
86
|
-
const relays = getNutzapInfoRelays(signed);
|
|
87
|
-
await publish(signed, relays);
|
|
80
|
+
const signed = await NutzapInfoFactory.modify(nutzapInfo).setMints(mints).sign(signer);
|
|
81
|
+
await publish(signed, getNutzapInfoRelays(signed));
|
|
88
82
|
};
|
|
89
83
|
}
|
|
90
84
|
/**
|
|
@@ -92,11 +86,11 @@ export function SetNutzapInfoMints(mints) {
|
|
|
92
86
|
* @throws if the nutzap info does not exist
|
|
93
87
|
*/
|
|
94
88
|
export function SetNutzapInfoRelays(relays) {
|
|
95
|
-
return async ({ events, self,
|
|
89
|
+
return async ({ events, self, signer, publish }) => {
|
|
96
90
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
97
91
|
if (!nutzapInfo)
|
|
98
92
|
throw new Error("Nutzap info does not exist");
|
|
99
|
-
const signed = await
|
|
93
|
+
const signed = await NutzapInfoFactory.modify(nutzapInfo).setRelays(relays).sign(signer);
|
|
100
94
|
await publish(signed, relays);
|
|
101
95
|
};
|
|
102
96
|
}
|
|
@@ -105,13 +99,11 @@ export function SetNutzapInfoRelays(relays) {
|
|
|
105
99
|
* @throws if the nutzap info does not exist
|
|
106
100
|
*/
|
|
107
101
|
export function SetNutzapInfoPubkey(privateKey) {
|
|
108
|
-
return async ({ events, self,
|
|
102
|
+
return async ({ events, self, signer, publish }) => {
|
|
109
103
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
110
104
|
if (!nutzapInfo)
|
|
111
105
|
throw new Error("Nutzap info does not exist");
|
|
112
|
-
const signed = await
|
|
113
|
-
|
|
114
|
-
const relays = getNutzapInfoRelays(signed);
|
|
115
|
-
await publish(signed, relays);
|
|
106
|
+
const signed = await NutzapInfoFactory.modify(nutzapInfo).setPubkey(privateKey).sign(signer);
|
|
107
|
+
await publish(signed, getNutzapInfoRelays(signed));
|
|
116
108
|
};
|
|
117
109
|
}
|
package/dist/actions/nutzaps.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { sumProofs, Wallet } from "@cashu/cashu-ts";
|
|
2
2
|
import { castUser } from "applesauce-common/casts";
|
|
3
3
|
import { bytesToHex } from "applesauce-core/helpers/event";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { WalletHistoryFactory } from "../factories/history.js";
|
|
5
|
+
import { WalletTokenFactory } from "../factories/tokens.js";
|
|
6
|
+
import { NutzapFactory } from "../factories/nutzap.js";
|
|
7
7
|
import { verifyProofsLocked } from "../helpers/nutzap-info.js";
|
|
8
8
|
import { getNutzapMint, getNutzapProofs, isValidNutzap } from "../helpers/nutzap.js";
|
|
9
9
|
import { getUnlockedWallet } from "./common.js";
|
|
@@ -11,7 +11,9 @@ import { getUnlockedWallet } from "./common.js";
|
|
|
11
11
|
import "../casts/__register__.js";
|
|
12
12
|
/** Creates a NIP-61 nutzap event for an event with a token */
|
|
13
13
|
export function NutzapEvent(event, token, options) {
|
|
14
|
-
return async ({ events,
|
|
14
|
+
return async ({ events, signer, user, publish }) => {
|
|
15
|
+
if (!signer)
|
|
16
|
+
throw new Error("Missing signer");
|
|
15
17
|
const { comment, couch } = options ?? {};
|
|
16
18
|
const clearStoredToken = await couch?.store(token);
|
|
17
19
|
try {
|
|
@@ -25,7 +27,7 @@ export function NutzapEvent(event, token, options) {
|
|
|
25
27
|
// Verify all tokens are p2pk locked
|
|
26
28
|
verifyProofsLocked(token.proofs, info.event);
|
|
27
29
|
// Create the nutzap event
|
|
28
|
-
const nutzap = await
|
|
30
|
+
const nutzap = await NutzapFactory.forEvent(event, token, comment || token.memo).sign(signer);
|
|
29
31
|
// Publish the nutzap event
|
|
30
32
|
await publish(nutzap, wallet.relays);
|
|
31
33
|
}
|
|
@@ -35,7 +37,9 @@ export function NutzapEvent(event, token, options) {
|
|
|
35
37
|
}
|
|
36
38
|
/** Creates a NIP-61 nutzap event to a users profile */
|
|
37
39
|
export function NutzapProfile(user, token, options) {
|
|
38
|
-
return async ({ events,
|
|
40
|
+
return async ({ events, signer, publish }) => {
|
|
41
|
+
if (!signer)
|
|
42
|
+
throw new Error("Missing signer");
|
|
39
43
|
const { comment, couch } = options ?? {};
|
|
40
44
|
const clearStoredToken = await couch?.store(token);
|
|
41
45
|
try {
|
|
@@ -47,7 +51,7 @@ export function NutzapProfile(user, token, options) {
|
|
|
47
51
|
// Verify all tokens are p2pk locked
|
|
48
52
|
verifyProofsLocked(token.proofs, info.event);
|
|
49
53
|
// Create the nutzap event
|
|
50
|
-
const nutzap = await
|
|
54
|
+
const nutzap = await NutzapFactory.forProfile(recipient, token, comment || token.memo).sign(signer);
|
|
51
55
|
// Publish the nutzap event
|
|
52
56
|
await publish(nutzap, info.relays);
|
|
53
57
|
}
|
|
@@ -63,7 +67,7 @@ export function NutzapProfile(user, token, options) {
|
|
|
63
67
|
* @param couch optional couch interface for temporarily storing tokens during the operation
|
|
64
68
|
*/
|
|
65
69
|
export function ReceiveNutzaps(nutzaps, couch) {
|
|
66
|
-
return async ({
|
|
70
|
+
return async ({ signer, user, publish }) => {
|
|
67
71
|
if (!signer)
|
|
68
72
|
throw new Error("Missing signer");
|
|
69
73
|
// Normalize to array
|
|
@@ -133,12 +137,17 @@ export function ReceiveNutzaps(nutzaps, couch) {
|
|
|
133
137
|
if (clearStoredToken) {
|
|
134
138
|
clearMethods.push(clearStoredToken);
|
|
135
139
|
}
|
|
136
|
-
const tokenEvent = await
|
|
140
|
+
const tokenEvent = await WalletTokenFactory.create(receivedToken, []).sign(signer);
|
|
137
141
|
// Create history event marking nutzap events as redeemed
|
|
138
142
|
const nutzapIds = mintNutzaps.map((n) => n.id);
|
|
139
|
-
const history = await
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
const history = await WalletHistoryFactory.create({
|
|
144
|
+
direction: "in",
|
|
145
|
+
amount,
|
|
146
|
+
mint,
|
|
147
|
+
created: [tokenEvent.id],
|
|
148
|
+
})
|
|
149
|
+
.redeemed(nutzapIds)
|
|
150
|
+
.sign(signer);
|
|
142
151
|
// Publish events
|
|
143
152
|
await publish(tokenEvent, wallet.relays);
|
|
144
153
|
await publish(history, wallet.relays);
|
package/dist/actions/tokens.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CheckStateEnum, sumProofs, Wallet } from "@cashu/cashu-ts";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { DeleteFactory } from "applesauce-core/factories";
|
|
3
|
+
import { WalletHistoryFactory } from "../factories/history.js";
|
|
4
|
+
import { WalletTokenFactory } from "../factories/tokens.js";
|
|
5
5
|
import { getProofUID, ignoreDuplicateProofs } from "../helpers/cashu.js";
|
|
6
6
|
import { dumbTokenSelection, getTokenContent, isTokenContentUnlocked, unlockTokenContent, WALLET_TOKEN_KIND, } from "../helpers/tokens.js";
|
|
7
7
|
import { getUnlockedWallet } from "./common.js";
|
|
@@ -14,16 +14,25 @@ import "../casts/__register__.js";
|
|
|
14
14
|
*/
|
|
15
15
|
export function AddToken(token, options) {
|
|
16
16
|
const { redeemed, fee, addHistory = true } = options ?? {};
|
|
17
|
-
return async ({
|
|
17
|
+
return async ({ signer, user, publish }) => {
|
|
18
|
+
if (!signer)
|
|
19
|
+
throw new Error("Missing signer");
|
|
18
20
|
const wallet = await getUnlockedWallet(user, signer);
|
|
19
21
|
const amount = sumProofs(token.proofs);
|
|
20
22
|
// Create the token and history events
|
|
21
|
-
const tokenEvent = await
|
|
23
|
+
const tokenEvent = await WalletTokenFactory.create(token).sign(signer);
|
|
22
24
|
let history;
|
|
23
25
|
if (addHistory || redeemed?.length) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
let historyFactory = WalletHistoryFactory.create({
|
|
27
|
+
direction: "in",
|
|
28
|
+
amount,
|
|
29
|
+
mint: token.mint,
|
|
30
|
+
created: [tokenEvent.id],
|
|
31
|
+
fee,
|
|
32
|
+
});
|
|
33
|
+
if (redeemed?.length)
|
|
34
|
+
historyFactory = historyFactory.redeemed(redeemed);
|
|
35
|
+
history = await historyFactory.sign(signer);
|
|
27
36
|
}
|
|
28
37
|
// Publish the events
|
|
29
38
|
await publish([tokenEvent, history].filter((e) => !!e), wallet.relays);
|
|
@@ -61,21 +70,23 @@ export function ReceiveToken(token, options) {
|
|
|
61
70
|
}
|
|
62
71
|
/** An action that deletes old tokens and creates a new one but does not add a history event */
|
|
63
72
|
export function RolloverTokens(tokens, token) {
|
|
64
|
-
return async ({
|
|
73
|
+
return async ({ signer, user, publish }) => {
|
|
74
|
+
if (!signer)
|
|
75
|
+
throw new Error("Missing signer");
|
|
65
76
|
const wallet = await getUnlockedWallet(user, signer);
|
|
66
77
|
// create a new token event
|
|
67
|
-
const tokenEvent = await
|
|
68
|
-
.create(WalletTokenBlueprint, token, tokens.map((e) => e.id))
|
|
69
|
-
.then(sign);
|
|
78
|
+
const tokenEvent = await WalletTokenFactory.create(token, tokens.map((e) => e.id)).sign(signer);
|
|
70
79
|
// create a delete event for old tokens
|
|
71
|
-
const
|
|
80
|
+
const deleteEvent = await DeleteFactory.fromEvents(tokens).sign(signer);
|
|
72
81
|
// publish events
|
|
73
|
-
await publish([tokenEvent,
|
|
82
|
+
await publish([tokenEvent, deleteEvent], wallet.relays);
|
|
74
83
|
};
|
|
75
84
|
}
|
|
76
85
|
/** An action that deletes old token events and adds a spend history item */
|
|
77
86
|
export function CompleteSpend(spent, change, couch) {
|
|
78
|
-
return async ({
|
|
87
|
+
return async ({ signer, user, publish }) => {
|
|
88
|
+
if (!signer)
|
|
89
|
+
throw new Error("Missing signer");
|
|
79
90
|
if (spent.length === 0)
|
|
80
91
|
throw new Error("Cant complete spent with no token events");
|
|
81
92
|
const unlocked = spent.filter(isTokenContentUnlocked);
|
|
@@ -91,20 +102,23 @@ export function CompleteSpend(spent, change, couch) {
|
|
|
91
102
|
try {
|
|
92
103
|
// create a new token event if needed
|
|
93
104
|
const tokenEvent = changeAmount > 0
|
|
94
|
-
? await
|
|
95
|
-
.create(WalletTokenBlueprint, change, spent.map((e) => e.id))
|
|
96
|
-
.then(sign)
|
|
105
|
+
? await WalletTokenFactory.create(change, spent.map((e) => e.id)).sign(signer)
|
|
97
106
|
: undefined;
|
|
98
107
|
// Get tokens total amount
|
|
99
108
|
const total = sumProofs(unlocked.map((s) => getTokenContent(s).proofs).flat());
|
|
100
109
|
// calculate the amount that was spent
|
|
101
110
|
const diff = total - changeAmount;
|
|
102
111
|
// sign delete and token
|
|
103
|
-
const deleteEvent = await
|
|
112
|
+
const deleteEvent = await DeleteFactory.fromEvents(spent).sign(signer);
|
|
104
113
|
// create a history entry
|
|
105
|
-
const history = await
|
|
106
|
-
|
|
107
|
-
.
|
|
114
|
+
const history = await WalletHistoryFactory.create({
|
|
115
|
+
direction: "out",
|
|
116
|
+
mint: change.mint,
|
|
117
|
+
amount: diff,
|
|
118
|
+
created: tokenEvent ? [tokenEvent.id] : [],
|
|
119
|
+
})
|
|
120
|
+
.redeemed([])
|
|
121
|
+
.sign(signer);
|
|
108
122
|
// publish events
|
|
109
123
|
await publish([tokenEvent, deleteEvent, history].filter((e) => !!e), wallet.relays);
|
|
110
124
|
// Clear the stored token from the couch after successful completion
|
|
@@ -118,13 +132,13 @@ export function CompleteSpend(spent, change, couch) {
|
|
|
118
132
|
}
|
|
119
133
|
/** Combines all unlocked token events into a single event per mint */
|
|
120
134
|
export function ConsolidateTokens(options) {
|
|
121
|
-
return async ({ events,
|
|
135
|
+
return async ({ events, signer, self, user, publish }) => {
|
|
136
|
+
if (!signer)
|
|
137
|
+
throw new Error("Missing signer");
|
|
122
138
|
const wallet = await getUnlockedWallet(user, signer);
|
|
123
139
|
const tokens = Array.from(events.getByFilters({ kinds: [WALLET_TOKEN_KIND], authors: [self] }));
|
|
124
140
|
// Unlock tokens if requested
|
|
125
141
|
if (options?.unlockTokens) {
|
|
126
|
-
if (!signer)
|
|
127
|
-
throw new Error("Missing signer");
|
|
128
142
|
for (const token of tokens) {
|
|
129
143
|
if (!isTokenContentUnlocked(token)) {
|
|
130
144
|
try {
|
|
@@ -154,7 +168,7 @@ export function ConsolidateTokens(options) {
|
|
|
154
168
|
.filter(ignoreDuplicateProofs());
|
|
155
169
|
// If there are no proofs, just delete the old tokens without interacting with the mint
|
|
156
170
|
if (proofs.length === 0) {
|
|
157
|
-
const deleteEvent = await
|
|
171
|
+
const deleteEvent = await DeleteFactory.fromEvents(tokens).sign(signer);
|
|
158
172
|
await publish(deleteEvent, wallet.relays);
|
|
159
173
|
continue;
|
|
160
174
|
}
|
|
@@ -166,12 +180,10 @@ export function ConsolidateTokens(options) {
|
|
|
166
180
|
const notSpent = proofs.filter((_, i) => states[i].state !== CheckStateEnum.SPENT);
|
|
167
181
|
// Only create a token event if there are unspent proofs
|
|
168
182
|
const tokenEvent = notSpent.length > 0
|
|
169
|
-
? await
|
|
170
|
-
.create(WalletTokenBlueprint, { mint, proofs: notSpent }, tokens.map((t) => t.id))
|
|
171
|
-
.then(sign)
|
|
183
|
+
? await WalletTokenFactory.create({ mint, proofs: notSpent }, tokens.map((t) => t.id)).sign(signer)
|
|
172
184
|
: undefined;
|
|
173
185
|
// create delete event
|
|
174
|
-
const deleteEvent = await
|
|
186
|
+
const deleteEvent = await DeleteFactory.fromEvents(tokens).sign(signer);
|
|
175
187
|
// Publish events
|
|
176
188
|
await publish([tokenEvent, deleteEvent].filter((e) => !!e), wallet.relays);
|
|
177
189
|
}
|
|
@@ -183,7 +195,9 @@ export function ConsolidateTokens(options) {
|
|
|
183
195
|
* @param couch the couch interface to recover tokens from
|
|
184
196
|
*/
|
|
185
197
|
export function RecoverFromCouch(couch) {
|
|
186
|
-
return async ({ events,
|
|
198
|
+
return async ({ events, signer, self, user, publish }) => {
|
|
199
|
+
if (!signer)
|
|
200
|
+
throw new Error("Missing signer");
|
|
187
201
|
const wallet = await getUnlockedWallet(user, signer);
|
|
188
202
|
// Get all tokens from the couch
|
|
189
203
|
const couchTokens = await couch.getAll();
|
|
@@ -192,14 +206,12 @@ export function RecoverFromCouch(couch) {
|
|
|
192
206
|
// Get all token events from the wallet
|
|
193
207
|
const walletTokens = Array.from(events.getByFilters({ kinds: [WALLET_TOKEN_KIND], authors: [self] }));
|
|
194
208
|
// Unlock wallet tokens if needed
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
await unlockTokenContent(token, signer);
|
|
200
|
-
}
|
|
201
|
-
catch { }
|
|
209
|
+
for (const token of walletTokens) {
|
|
210
|
+
if (!isTokenContentUnlocked(token)) {
|
|
211
|
+
try {
|
|
212
|
+
await unlockTokenContent(token, signer);
|
|
202
213
|
}
|
|
214
|
+
catch { }
|
|
203
215
|
}
|
|
204
216
|
}
|
|
205
217
|
// Collect all proofs from wallet tokens
|
|
@@ -247,7 +259,7 @@ export function RecoverFromCouch(couch) {
|
|
|
247
259
|
proofs: unspentProofs,
|
|
248
260
|
unit: tokens[0]?.unit,
|
|
249
261
|
};
|
|
250
|
-
const tokenEvent = await
|
|
262
|
+
const tokenEvent = await WalletTokenFactory.create(recoveredToken).sign(signer);
|
|
251
263
|
// Publish the token event
|
|
252
264
|
await publish(tokenEvent, wallet.relays);
|
|
253
265
|
// Clear the token from the couch
|
package/dist/actions/wallet.js
CHANGED
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WalletBackupFactory, WalletFactory } from "../factories/wallet.js";
|
|
2
|
+
import { NutzapInfoFactory } from "../factories/nutzap-info.js";
|
|
2
3
|
import { unlockHistoryContent, WALLET_HISTORY_KIND } from "../helpers/history.js";
|
|
3
4
|
import { NUTZAP_INFO_KIND } from "../helpers/nutzap-info.js";
|
|
4
5
|
import { unlockTokenContent, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
|
|
5
6
|
import { getWalletMints, unlockWallet, WALLET_KIND } from "../helpers/wallet.js";
|
|
6
|
-
import { setNutzapInfoPubkey } from "../operations/nutzap-info.js";
|
|
7
|
-
import { setMints, setRelays } from "../operations/wallet.js";
|
|
8
7
|
import { getUnlockedWallet } from "./common.js";
|
|
9
8
|
// Make sure the wallet$ is registered on the user class
|
|
10
9
|
import "../casts/__register__.js";
|
|
11
10
|
/** An action that creates a new 17375 wallet event and 375 wallet backup */
|
|
12
11
|
export function CreateWallet({ mints, privateKey, relays, }) {
|
|
13
|
-
return async ({ events,
|
|
12
|
+
return async ({ events, signer, self, publish }) => {
|
|
14
13
|
if (mints.length === 0)
|
|
15
14
|
throw new Error("At least one mint is required");
|
|
16
15
|
const existing = events.getReplaceable(WALLET_KIND, self);
|
|
17
16
|
if (existing)
|
|
18
17
|
throw new Error("Wallet already exists");
|
|
19
18
|
// Create new wallet event
|
|
20
|
-
const wallet = await
|
|
19
|
+
const wallet = await WalletFactory.create(mints, privateKey, relays).sign(signer);
|
|
21
20
|
// Setup nutzap info event
|
|
22
21
|
if (privateKey) {
|
|
23
22
|
// Create a backup event if a private key is provided
|
|
24
|
-
const backup = await
|
|
23
|
+
const backup = await WalletBackupFactory.create(wallet).sign(signer);
|
|
25
24
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
26
25
|
// Always set pubkey if private key is provided (create or update)
|
|
27
|
-
const
|
|
28
|
-
? await
|
|
29
|
-
: await
|
|
30
|
-
const info = await factory.sign(nutzapInfoDraft);
|
|
26
|
+
const info = nutzapInfo
|
|
27
|
+
? await NutzapInfoFactory.modify(nutzapInfo).setPubkey(privateKey).sign(signer)
|
|
28
|
+
: await NutzapInfoFactory.create().setPubkey(privateKey).sign(signer);
|
|
31
29
|
// Publish all events at the same time
|
|
32
30
|
await publish([wallet, backup, info], relays);
|
|
33
31
|
}
|
|
@@ -42,30 +40,25 @@ export function CreateWallet({ mints, privateKey, relays, }) {
|
|
|
42
40
|
* @throws if the wallet does not exist or cannot be unlocked
|
|
43
41
|
*/
|
|
44
42
|
export function WalletAddPrivateKey(privateKey, override = false) {
|
|
45
|
-
return async ({ events, self,
|
|
43
|
+
return async ({ events, self, signer, user, publish }) => {
|
|
46
44
|
const wallet = await getUnlockedWallet(user, signer);
|
|
47
45
|
if (wallet.privateKey && override !== true)
|
|
48
46
|
throw new Error("Wallet already has a private key");
|
|
49
|
-
const signed = await
|
|
50
|
-
.create(WalletBlueprint, { mints: getWalletMints(wallet.event), privateKey })
|
|
51
|
-
.then(sign);
|
|
47
|
+
const signed = await WalletFactory.create(getWalletMints(wallet.event), privateKey).as(signer).sign();
|
|
52
48
|
// create backup event for wallet
|
|
53
|
-
const backup = await
|
|
49
|
+
const backup = await WalletBackupFactory.create(signed).sign(signer);
|
|
54
50
|
// set nutzap info pubkey for receiving nutzaps
|
|
55
51
|
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
56
52
|
const info = nutzapInfo
|
|
57
|
-
? await
|
|
58
|
-
: await
|
|
53
|
+
? await NutzapInfoFactory.modify(nutzapInfo).setPubkey(privateKey).sign(signer)
|
|
54
|
+
: await NutzapInfoFactory.create().setPubkey(privateKey).sign(signer);
|
|
59
55
|
// publish all events at the same time
|
|
60
56
|
await publish([signed, backup, info], wallet.relays);
|
|
61
57
|
};
|
|
62
58
|
}
|
|
63
59
|
/** Unlocks the wallet event and optionally the tokens and history events */
|
|
64
60
|
export function UnlockWallet(unlock) {
|
|
65
|
-
return async ({ events, self,
|
|
66
|
-
const signer = factory.context.signer;
|
|
67
|
-
if (!signer)
|
|
68
|
-
throw new Error("Missing signer");
|
|
61
|
+
return async ({ events, self, signer }) => {
|
|
69
62
|
const wallet = events.getReplaceable(WALLET_KIND, self);
|
|
70
63
|
if (!wallet)
|
|
71
64
|
throw new Error("Wallet does not exist");
|
|
@@ -87,9 +80,9 @@ export function UnlockWallet(unlock) {
|
|
|
87
80
|
* @throws if the wallet does not exist or cannot be unlocked
|
|
88
81
|
*/
|
|
89
82
|
export function SetWalletMints(mints) {
|
|
90
|
-
return async ({ user, signer,
|
|
83
|
+
return async ({ user, signer, publish }) => {
|
|
91
84
|
const wallet = await getUnlockedWallet(user, signer);
|
|
92
|
-
const signed = await
|
|
85
|
+
const signed = await WalletFactory.modify(wallet.event).mints(mints).sign(signer);
|
|
93
86
|
await publish(signed, wallet.relays);
|
|
94
87
|
};
|
|
95
88
|
}
|
|
@@ -98,9 +91,9 @@ export function SetWalletMints(mints) {
|
|
|
98
91
|
* @throws if the wallet does not exist or cannot be unlocked
|
|
99
92
|
*/
|
|
100
93
|
export function SetWalletRelays(relays) {
|
|
101
|
-
return async ({ user, signer,
|
|
94
|
+
return async ({ user, signer, publish }) => {
|
|
102
95
|
const wallet = await getUnlockedWallet(user, signer);
|
|
103
|
-
const signed = await
|
|
96
|
+
const signed = await WalletFactory.modify(wallet.event).relays(relays.map(String)).sign(signer);
|
|
104
97
|
await publish(signed, relays.map(String));
|
|
105
98
|
};
|
|
106
99
|
}
|
|
@@ -3,7 +3,7 @@ import { NutzapInfo } from "./nutzap-info.js";
|
|
|
3
3
|
import { Nutzap } from "./nutzap.js";
|
|
4
4
|
import { Wallet } from "./wallet.js";
|
|
5
5
|
import { MintRecommendation } from "./mint-recommendation.js";
|
|
6
|
-
declare module "applesauce-
|
|
6
|
+
declare module "applesauce-core/casts" {
|
|
7
7
|
interface User {
|
|
8
8
|
readonly wallet$: ChainableObservable<Wallet | undefined>;
|
|
9
9
|
readonly nutzap$: ChainableObservable<NutzapInfo | undefined>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Note, Stream } from "applesauce-common/casts";
|
|
2
|
-
import { User } from "applesauce-
|
|
2
|
+
import { User } from "applesauce-core/casts";
|
|
3
3
|
import { castEventStream, castTimelineStream } from "applesauce-common/observable";
|
|
4
4
|
import { buildCommonEventRelationFilters } from "applesauce-core/helpers";
|
|
5
5
|
import { switchMap } from "rxjs";
|
|
@@ -2,6 +2,7 @@ import { CastRefEventStore, EventCast } from "applesauce-common/casts";
|
|
|
2
2
|
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
3
3
|
import { CashuMintInfoEvent } from "../helpers/mint-info.js";
|
|
4
4
|
import { MintRecommendation } from "./mint-recommendation.js";
|
|
5
|
+
/** A cast for a NIP-87 cashu mint info event */
|
|
5
6
|
export declare class MintInfo extends EventCast<CashuMintInfoEvent> {
|
|
6
7
|
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
7
8
|
/** The mint's pubkey (from the `d` tag) */
|
package/dist/casts/mint-info.js
CHANGED
|
@@ -3,10 +3,12 @@ import { castTimelineStream } from "applesauce-common/observable";
|
|
|
3
3
|
import { getCashuMintMetadata, getCashuMintNetwork, getCashuMintNuts, getCashuMintPubkey, getCashuMintURL, isValidCashuMintInfo, } from "../helpers/mint-info.js";
|
|
4
4
|
import { MINT_RECOMMENDATION_KIND } from "../helpers/mint-recommendation.js";
|
|
5
5
|
import { MintRecommendation } from "./mint-recommendation.js";
|
|
6
|
+
/** A cast for a NIP-87 cashu mint info event */
|
|
6
7
|
export class MintInfo extends EventCast {
|
|
7
8
|
constructor(event, store) {
|
|
8
|
-
if (!isValidCashuMintInfo(event))
|
|
9
|
+
if (!isValidCashuMintInfo(event)) {
|
|
9
10
|
throw new Error("Invalid cashu mint info event");
|
|
11
|
+
}
|
|
10
12
|
super(event, store);
|
|
11
13
|
}
|
|
12
14
|
/** The mint's pubkey (from the `d` tag) */
|