applesauce-wallet-connect 0.0.0-next-20250808173123
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/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/tokens.d.ts +17 -0
- package/dist/actions/tokens.js +110 -0
- package/dist/actions/wallet.d.ts +13 -0
- package/dist/actions/wallet.js +64 -0
- package/dist/actions/zap-info.d.ts +22 -0
- package/dist/actions/zap-info.js +83 -0
- package/dist/actions/zaps.d.ts +8 -0
- package/dist/actions/zaps.js +30 -0
- package/dist/blueprints/history.d.ts +4 -0
- package/dist/blueprints/history.js +11 -0
- package/dist/blueprints/index.d.ts +4 -0
- package/dist/blueprints/index.js +4 -0
- package/dist/blueprints/info.d.ts +4 -0
- package/dist/blueprints/info.js +8 -0
- package/dist/blueprints/notification.d.ts +15 -0
- package/dist/blueprints/notification.js +21 -0
- package/dist/blueprints/request.d.ts +9 -0
- package/dist/blueprints/request.js +12 -0
- package/dist/blueprints/response.d.ts +5 -0
- package/dist/blueprints/response.js +10 -0
- package/dist/blueprints/support.d.ts +4 -0
- package/dist/blueprints/support.js +8 -0
- package/dist/blueprints/tokens.d.ts +7 -0
- package/dist/blueprints/tokens.js +11 -0
- package/dist/blueprints/wallet.d.ts +5 -0
- package/dist/blueprints/wallet.js +11 -0
- package/dist/blueprints/zaps.d.ts +8 -0
- package/dist/blueprints/zaps.js +12 -0
- package/dist/helpers/animated-qr.d.ts +30 -0
- package/dist/helpers/animated-qr.js +71 -0
- package/dist/helpers/connect-uri.d.ts +12 -0
- package/dist/helpers/connect-uri.js +23 -0
- package/dist/helpers/encryption.d.ts +2 -0
- package/dist/helpers/encryption.js +1 -0
- package/dist/helpers/error.d.ts +55 -0
- package/dist/helpers/error.js +81 -0
- package/dist/helpers/history.d.ts +26 -0
- package/dist/helpers/history.js +47 -0
- package/dist/helpers/index.d.ts +7 -0
- package/dist/helpers/index.js +7 -0
- package/dist/helpers/info.d.ts +34 -0
- package/dist/helpers/info.js +97 -0
- package/dist/helpers/methods.d.ts +1 -0
- package/dist/helpers/methods.js +1 -0
- package/dist/helpers/notification.d.ts +28 -0
- package/dist/helpers/notification.js +28 -0
- package/dist/helpers/nutzap.d.ts +27 -0
- package/dist/helpers/nutzap.js +66 -0
- package/dist/helpers/request.d.ts +131 -0
- package/dist/helpers/request.js +51 -0
- package/dist/helpers/response.d.ts +138 -0
- package/dist/helpers/response.js +35 -0
- package/dist/helpers/support.d.ts +34 -0
- package/dist/helpers/support.js +97 -0
- package/dist/helpers/tokens.d.ts +58 -0
- package/dist/helpers/tokens.js +162 -0
- package/dist/helpers/wallet.d.ts +15 -0
- package/dist/helpers/wallet.js +41 -0
- package/dist/helpers/zap-info.d.ts +19 -0
- package/dist/helpers/zap-info.js +42 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/interface.d.ts +6 -0
- package/dist/interface.js +1 -0
- package/dist/models/history.d.ts +6 -0
- package/dist/models/history.js +21 -0
- package/dist/models/index.d.ts +4 -0
- package/dist/models/index.js +4 -0
- package/dist/models/nutzap.d.ts +6 -0
- package/dist/models/nutzap.js +16 -0
- package/dist/models/tokens.d.ts +6 -0
- package/dist/models/tokens.js +58 -0
- package/dist/models/wallet.d.ts +13 -0
- package/dist/models/wallet.js +18 -0
- package/dist/operations/history.d.ts +7 -0
- package/dist/operations/history.js +34 -0
- package/dist/operations/index.d.ts +5 -0
- package/dist/operations/index.js +5 -0
- package/dist/operations/nutzap.d.ts +14 -0
- package/dist/operations/nutzap.js +33 -0
- package/dist/operations/tokens.d.ts +4 -0
- package/dist/operations/tokens.js +24 -0
- package/dist/operations/wallet.d.ts +8 -0
- package/dist/operations/wallet.js +30 -0
- package/dist/operations/zap-info.d.ts +10 -0
- package/dist/operations/zap-info.js +17 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +1 -0
- package/dist/wallet-connect.d.ts +111 -0
- package/dist/wallet-connect.js +271 -0
- package/dist/wallet-service.d.ts +111 -0
- package/dist/wallet-service.js +270 -0
- package/package.json +83 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 hzrd149
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# applesauce-wallet-connect
|
|
2
|
+
|
|
3
|
+
NIP-47 Nostr Wallet Connect implementation for both clients and services.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **WalletConnect**: Client-side implementation for connecting to NIP-47 wallet services
|
|
8
|
+
- **WalletService**: Service-side implementation for creating NIP-47 wallet services
|
|
9
|
+
- Full support for all NIP-47 methods
|
|
10
|
+
- TypeScript support with full type safety
|
|
11
|
+
- RxJS-based reactive architecture
|
|
12
|
+
- Support for both NIP-04 and NIP-44 encryption
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install applesauce-wallet-connect
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## WalletConnect (Client)
|
|
21
|
+
|
|
22
|
+
Connect to a wallet service using a connection string:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { WalletConnect } from "applesauce-wallet-connect";
|
|
26
|
+
|
|
27
|
+
const wallet = WalletConnect.fromConnectionString("nostr+walletconnect://relay.example.com?secret=...&pubkey=...");
|
|
28
|
+
|
|
29
|
+
// Pay an invoice
|
|
30
|
+
const result = await wallet.payInvoice("lnbc1...");
|
|
31
|
+
console.log("Payment preimage:", result.preimage);
|
|
32
|
+
|
|
33
|
+
// Get wallet balance
|
|
34
|
+
const balance = await wallet.getBalance();
|
|
35
|
+
console.log("Balance:", balance.balance, "msats");
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## WalletService (Service)
|
|
39
|
+
|
|
40
|
+
Create a wallet service that handles NIP-47 requests:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { WalletService } from "applesauce-wallet-connect";
|
|
44
|
+
import { SimpleSigner } from "applesauce-signers";
|
|
45
|
+
|
|
46
|
+
// Create a signer for the service
|
|
47
|
+
const signer = new SimpleSigner();
|
|
48
|
+
|
|
49
|
+
// Define method handlers
|
|
50
|
+
const handlers = {
|
|
51
|
+
get_info: async () => ({
|
|
52
|
+
alias: "My Wallet",
|
|
53
|
+
color: "#ff0000",
|
|
54
|
+
pubkey: await signer.getPublicKey(),
|
|
55
|
+
network: "mainnet" as const,
|
|
56
|
+
block_height: 800000,
|
|
57
|
+
block_hash: "0000...0000",
|
|
58
|
+
methods: ["get_info", "get_balance", "pay_invoice"],
|
|
59
|
+
}),
|
|
60
|
+
|
|
61
|
+
get_balance: async () => ({
|
|
62
|
+
balance: 100000, // 100 sats in msats
|
|
63
|
+
}),
|
|
64
|
+
|
|
65
|
+
pay_invoice: async (params) => {
|
|
66
|
+
// Implement your payment logic here
|
|
67
|
+
console.log("Paying invoice:", params.invoice);
|
|
68
|
+
return {
|
|
69
|
+
preimage: "payment_preimage_here",
|
|
70
|
+
fees_paid: 1000,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Create the service
|
|
76
|
+
const service = new WalletService({
|
|
77
|
+
subscriptionMethod: mySubscriptionMethod,
|
|
78
|
+
publishMethod: myPublishMethod,
|
|
79
|
+
relays: ["wss://relay.example.com"],
|
|
80
|
+
signer,
|
|
81
|
+
handlers,
|
|
82
|
+
// Supported encryption methods will be set based on the signer
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Start the service
|
|
86
|
+
await service.start();
|
|
87
|
+
console.log("Wallet service started");
|
|
88
|
+
|
|
89
|
+
// Get the connection string for the wallet service
|
|
90
|
+
console.log(service.getConnectionString());
|
|
91
|
+
|
|
92
|
+
// Stop the service when done
|
|
93
|
+
// service.stop();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Supported Methods
|
|
97
|
+
|
|
98
|
+
Both WalletConnect and WalletService support all NIP-47 methods:
|
|
99
|
+
|
|
100
|
+
- `get_info` - Get wallet information
|
|
101
|
+
- `get_balance` - Get wallet balance
|
|
102
|
+
- `pay_invoice` - Pay a Lightning invoice
|
|
103
|
+
- `multi_pay_invoice` - Pay multiple invoices
|
|
104
|
+
- `pay_keysend` - Send a keysend payment
|
|
105
|
+
- `multi_pay_keysend` - Send multiple keysend payments
|
|
106
|
+
- `make_invoice` - Create a new invoice
|
|
107
|
+
- `lookup_invoice` - Look up an invoice
|
|
108
|
+
- `list_transactions` - List transactions
|
|
109
|
+
|
|
110
|
+
## Documentation
|
|
111
|
+
|
|
112
|
+
For more detailed documentation, visit [the docs](https://hzrd149.github.io/applesauce/).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
import { Action } from "applesauce-actions";
|
|
3
|
+
import { NostrEvent } from "nostr-tools";
|
|
4
|
+
/**
|
|
5
|
+
* Adds a cashu token to the wallet and marks a list of nutzaps as redeemed
|
|
6
|
+
* @param token the cashu token to add
|
|
7
|
+
* @param redeemed an array of nutzap event ids to mark as redeemed
|
|
8
|
+
*/
|
|
9
|
+
export declare function ReceiveToken(token: Token, redeemed?: string[], fee?: number): Action;
|
|
10
|
+
/** An action that deletes old tokens and creates a new one but does not add a history event */
|
|
11
|
+
export declare function RolloverTokens(tokens: NostrEvent[], token: Token): Action;
|
|
12
|
+
/** An action that deletes old token events and adds a spend history item */
|
|
13
|
+
export declare function CompleteSpend(spent: NostrEvent[], change: Token): Action;
|
|
14
|
+
/** Combines all unlocked token events into a single event per mint */
|
|
15
|
+
export declare function ConsolidateTokens(opts?: {
|
|
16
|
+
ignoreLocked?: boolean;
|
|
17
|
+
}): Action;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { CashuMint, CashuWallet, CheckStateEnum } from "@cashu/cashu-ts";
|
|
2
|
+
import { DeleteBlueprint } from "applesauce-factory/blueprints";
|
|
3
|
+
import { getTokenContent, ignoreDuplicateProofs, isTokenContentLocked, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
|
|
4
|
+
import { WalletTokenBlueprint } from "../blueprints/tokens.js";
|
|
5
|
+
import { WalletHistoryBlueprint } from "../blueprints/history.js";
|
|
6
|
+
/**
|
|
7
|
+
* Adds a cashu token to the wallet and marks a list of nutzaps as redeemed
|
|
8
|
+
* @param token the cashu token to add
|
|
9
|
+
* @param redeemed an array of nutzap event ids to mark as redeemed
|
|
10
|
+
*/
|
|
11
|
+
export function ReceiveToken(token, redeemed, fee) {
|
|
12
|
+
return async function* ({ factory }) {
|
|
13
|
+
const amount = token.proofs.reduce((t, p) => t + p.amount, 0);
|
|
14
|
+
const tokenEvent = await factory.sign(await factory.create(WalletTokenBlueprint, token, []));
|
|
15
|
+
const history = await factory.sign(await factory.create(WalletHistoryBlueprint, { direction: "in", amount, mint: token.mint, created: [tokenEvent.id], fee }, redeemed ?? []));
|
|
16
|
+
yield tokenEvent;
|
|
17
|
+
yield history;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** An action that deletes old tokens and creates a new one but does not add a history event */
|
|
21
|
+
export function RolloverTokens(tokens, token) {
|
|
22
|
+
return async function* ({ factory }) {
|
|
23
|
+
// create a delete event for old tokens
|
|
24
|
+
const deleteDraft = await factory.create(DeleteBlueprint, tokens);
|
|
25
|
+
// create a new token event
|
|
26
|
+
const tokenDraft = await factory.create(WalletTokenBlueprint, token, tokens.map((e) => e.id));
|
|
27
|
+
// sign events
|
|
28
|
+
const signedDelete = await factory.sign(deleteDraft);
|
|
29
|
+
const signedToken = await factory.sign(tokenDraft);
|
|
30
|
+
// publish events
|
|
31
|
+
yield signedDelete;
|
|
32
|
+
yield signedToken;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** An action that deletes old token events and adds a spend history item */
|
|
36
|
+
export function CompleteSpend(spent, change) {
|
|
37
|
+
return async function* ({ factory }) {
|
|
38
|
+
if (spent.length === 0)
|
|
39
|
+
throw new Error("Cant complete spent with no token events");
|
|
40
|
+
if (spent.some((s) => isTokenContentLocked(s)))
|
|
41
|
+
throw new Error("Cant complete spend with locked tokens");
|
|
42
|
+
// create the nip-09 delete event for previous events
|
|
43
|
+
const deleteDraft = await factory.create(DeleteBlueprint, spent);
|
|
44
|
+
const changeAmount = change.proofs.reduce((t, p) => t + p.amount, 0);
|
|
45
|
+
// create a new token event if needed
|
|
46
|
+
const changeDraft = changeAmount > 0
|
|
47
|
+
? await factory.create(WalletTokenBlueprint, change, spent.map((e) => e.id))
|
|
48
|
+
: undefined;
|
|
49
|
+
const total = spent.reduce((total, token) => total + getTokenContent(token).proofs.reduce((t, p) => t + p.amount, 0), 0);
|
|
50
|
+
// calculate the amount that was spent
|
|
51
|
+
const diff = total - changeAmount;
|
|
52
|
+
// sign delete and token
|
|
53
|
+
const signedDelete = await factory.sign(deleteDraft);
|
|
54
|
+
const signedToken = changeDraft && (await factory.sign(changeDraft));
|
|
55
|
+
// create a history entry
|
|
56
|
+
const history = await factory.create(WalletHistoryBlueprint, { direction: "out", mint: change.mint, amount: diff, created: signedToken ? [signedToken.id] : [] }, []);
|
|
57
|
+
// sign history
|
|
58
|
+
const signedHistory = await factory.sign(history);
|
|
59
|
+
// publish events
|
|
60
|
+
yield signedDelete;
|
|
61
|
+
if (signedToken)
|
|
62
|
+
yield signedToken;
|
|
63
|
+
yield signedHistory;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Combines all unlocked token events into a single event per mint */
|
|
67
|
+
export function ConsolidateTokens(opts) {
|
|
68
|
+
return async function* ({ events, factory, self }) {
|
|
69
|
+
const tokens = Array.from(events.getByFilters({ kinds: [WALLET_TOKEN_KIND], authors: [self] })).filter((token) => {
|
|
70
|
+
if (isTokenContentLocked(token)) {
|
|
71
|
+
if (opts?.ignoreLocked)
|
|
72
|
+
return false;
|
|
73
|
+
else
|
|
74
|
+
throw new Error("Token is locked");
|
|
75
|
+
}
|
|
76
|
+
else
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
const byMint = tokens.reduce((map, token) => {
|
|
80
|
+
const mint = getTokenContent(token).mint;
|
|
81
|
+
if (!map.has(mint))
|
|
82
|
+
map.set(mint, []);
|
|
83
|
+
map.get(mint).push(token);
|
|
84
|
+
return map;
|
|
85
|
+
}, new Map());
|
|
86
|
+
// loop over each mint and consolidate proofs
|
|
87
|
+
for (const [mint, tokens] of byMint) {
|
|
88
|
+
const cashuMint = new CashuMint(mint);
|
|
89
|
+
const cashuWallet = new CashuWallet(cashuMint);
|
|
90
|
+
// get all tokens proofs
|
|
91
|
+
const proofs = tokens
|
|
92
|
+
.map((t) => getTokenContent(t).proofs)
|
|
93
|
+
.flat()
|
|
94
|
+
// filter out duplicate proofs
|
|
95
|
+
.filter(ignoreDuplicateProofs());
|
|
96
|
+
// NOTE: this assumes that the states array is the same length and order as the proofs array
|
|
97
|
+
const states = await cashuWallet.checkProofsStates(proofs);
|
|
98
|
+
const notSpent = proofs.filter((_, i) => states[i].state !== CheckStateEnum.SPENT);
|
|
99
|
+
// create delete and token event
|
|
100
|
+
const deleteDraft = await factory.create(DeleteBlueprint, tokens);
|
|
101
|
+
const tokenDraft = await factory.create(WalletTokenBlueprint, { mint, proofs: notSpent }, tokens.map((t) => t.id));
|
|
102
|
+
// sign events
|
|
103
|
+
const signedToken = await factory.sign(tokenDraft);
|
|
104
|
+
const signedDelete = await factory.sign(deleteDraft);
|
|
105
|
+
// publish events for mint
|
|
106
|
+
yield signedToken;
|
|
107
|
+
yield signedDelete;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Action } from "applesauce-actions";
|
|
2
|
+
/** An action that creates a new 17375 wallet event and 375 wallet backup */
|
|
3
|
+
export declare function CreateWallet(mints: string[], privateKey?: Uint8Array): Action;
|
|
4
|
+
/**
|
|
5
|
+
* Adds a private key to a wallet event
|
|
6
|
+
* @throws if the wallet does not exist or is locked
|
|
7
|
+
*/
|
|
8
|
+
export declare function WalletAddPrivateKey(privateKey: Uint8Array): Action;
|
|
9
|
+
/** Unlocks the wallet event and optionally the tokens and history events */
|
|
10
|
+
export declare function UnlockWallet(unlock?: {
|
|
11
|
+
history?: boolean;
|
|
12
|
+
tokens?: boolean;
|
|
13
|
+
}): Action;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getWalletMints, getWalletPrivateKey, isWalletLocked, unlockWallet, WALLET_KIND } from "../helpers/wallet.js";
|
|
2
|
+
import { WalletBackupBlueprint, WalletBlueprint } from "../blueprints/wallet.js";
|
|
3
|
+
import { isTokenContentLocked, unlockTokenContent, WALLET_TOKEN_KIND } from "../helpers/tokens.js";
|
|
4
|
+
import { isHistoryContentLocked, unlockHistoryContent, WALLET_HISTORY_KIND } from "../helpers/history.js";
|
|
5
|
+
/** An action that creates a new 17375 wallet event and 375 wallet backup */
|
|
6
|
+
export function CreateWallet(mints, privateKey) {
|
|
7
|
+
return async function* ({ events, factory, self }) {
|
|
8
|
+
const existing = events.getReplaceable(WALLET_KIND, self);
|
|
9
|
+
if (existing)
|
|
10
|
+
throw new Error("Wallet already exists");
|
|
11
|
+
const wallet = await factory.sign(await factory.create(WalletBlueprint, mints, privateKey));
|
|
12
|
+
const backup = await factory.sign(await factory.create(WalletBackupBlueprint, wallet));
|
|
13
|
+
// publish the backup first
|
|
14
|
+
yield backup;
|
|
15
|
+
yield wallet;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Adds a private key to a wallet event
|
|
20
|
+
* @throws if the wallet does not exist or is locked
|
|
21
|
+
*/
|
|
22
|
+
export function WalletAddPrivateKey(privateKey) {
|
|
23
|
+
return async function* ({ events, self, factory }) {
|
|
24
|
+
const wallet = events.getReplaceable(WALLET_KIND, self);
|
|
25
|
+
if (!wallet)
|
|
26
|
+
throw new Error("Wallet does not exist");
|
|
27
|
+
if (isWalletLocked(wallet))
|
|
28
|
+
throw new Error("Wallet is locked");
|
|
29
|
+
if (getWalletPrivateKey(wallet))
|
|
30
|
+
throw new Error("Wallet already has a private key");
|
|
31
|
+
const draft = await factory.create(WalletBlueprint, getWalletMints(wallet), privateKey);
|
|
32
|
+
const signed = await factory.sign(draft);
|
|
33
|
+
// create backup event for wallet
|
|
34
|
+
const backup = await factory.sign(await factory.create(WalletBackupBlueprint, signed));
|
|
35
|
+
// publish events
|
|
36
|
+
yield backup;
|
|
37
|
+
yield signed;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Unlocks the wallet event and optionally the tokens and history events */
|
|
41
|
+
export function UnlockWallet(unlock) {
|
|
42
|
+
return async function* ({ events, self, factory }) {
|
|
43
|
+
const signer = factory.context.signer;
|
|
44
|
+
if (!signer)
|
|
45
|
+
throw new Error("Missing signer");
|
|
46
|
+
const wallet = events.getReplaceable(WALLET_KIND, self);
|
|
47
|
+
if (!wallet)
|
|
48
|
+
throw new Error("Wallet does not exist");
|
|
49
|
+
if (isWalletLocked(wallet))
|
|
50
|
+
await unlockWallet(wallet, signer);
|
|
51
|
+
if (unlock?.tokens) {
|
|
52
|
+
const tokens = events.getTimeline({ kinds: [WALLET_TOKEN_KIND], authors: [self] });
|
|
53
|
+
for (const token of tokens)
|
|
54
|
+
if (isTokenContentLocked(token))
|
|
55
|
+
await unlockTokenContent(token, signer);
|
|
56
|
+
}
|
|
57
|
+
if (unlock?.history) {
|
|
58
|
+
const history = events.getTimeline({ kinds: [WALLET_HISTORY_KIND], authors: [self] });
|
|
59
|
+
for (const entry of history)
|
|
60
|
+
if (isHistoryContentLocked(entry))
|
|
61
|
+
await unlockHistoryContent(entry, signer);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Action } from "applesauce-actions";
|
|
2
|
+
/** An action to add a relay to the kind 10019 nutzap info event */
|
|
3
|
+
export declare function AddNutzapInfoRelay(relay: string | string[]): Action;
|
|
4
|
+
/** An action to remove a relay from the kind 10019 nutzap info event */
|
|
5
|
+
export declare function RemoveNutzapInfoRelay(relay: string | string[]): Action;
|
|
6
|
+
/** An action to add a mint to the kind 10019 nutzap info event */
|
|
7
|
+
export declare function AddNutzapInfoMint(mint: {
|
|
8
|
+
url: string;
|
|
9
|
+
units?: string[];
|
|
10
|
+
} | Array<{
|
|
11
|
+
url: string;
|
|
12
|
+
units?: string[];
|
|
13
|
+
}>): Action;
|
|
14
|
+
/** An action to remove a mint from the kind 10019 nutzap info event */
|
|
15
|
+
export declare function RemoveNutzapInfoMint(mint: string | string[]): Action;
|
|
16
|
+
/** An action to set the pubkey for the kind 10019 nutzap info event */
|
|
17
|
+
export declare function SetNutzapInfoPubkey(pubkey: string): Action;
|
|
18
|
+
/** An action to update the entire nutzap info event */
|
|
19
|
+
export declare function UpdateNutzapInfo(relays: string[], mints: Array<{
|
|
20
|
+
url: string;
|
|
21
|
+
units?: string[];
|
|
22
|
+
}>, pubkey: string): Action;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { modifyPublicTags } from "applesauce-factory/operations";
|
|
2
|
+
import { addNameValueTag, removeNameValueTag, setSingletonTag } from "applesauce-factory/operations/tag";
|
|
3
|
+
import { NUTZAP_INFO_KIND } from "../helpers/zap-info.js";
|
|
4
|
+
import { setNutzapInfoMints, setNutzapInfoPubkey, setNutzapInfoRelays } from "../operations/zap-info.js";
|
|
5
|
+
/** An action to add a relay to the kind 10019 nutzap info event */
|
|
6
|
+
export function AddNutzapInfoRelay(relay) {
|
|
7
|
+
return async function* ({ events, factory, self }) {
|
|
8
|
+
if (typeof relay === "string")
|
|
9
|
+
relay = [relay];
|
|
10
|
+
const operations = relay.map((r) => addNameValueTag(["relay", r], false));
|
|
11
|
+
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
12
|
+
const draft = nutzapInfo
|
|
13
|
+
? await factory.modifyTags(nutzapInfo, ...operations)
|
|
14
|
+
: await factory.build({ kind: NUTZAP_INFO_KIND }, modifyPublicTags(...operations));
|
|
15
|
+
const signed = await factory.sign(draft);
|
|
16
|
+
yield signed;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** An action to remove a relay from the kind 10019 nutzap info event */
|
|
20
|
+
export function RemoveNutzapInfoRelay(relay) {
|
|
21
|
+
return async function* ({ events, factory, self }) {
|
|
22
|
+
if (typeof relay === "string")
|
|
23
|
+
relay = [relay];
|
|
24
|
+
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
25
|
+
if (!nutzapInfo)
|
|
26
|
+
return;
|
|
27
|
+
const draft = await factory.modifyTags(nutzapInfo, ...relay.map((r) => removeNameValueTag(["relay", r])));
|
|
28
|
+
const signed = await factory.sign(draft);
|
|
29
|
+
yield signed;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** An action to add a mint to the kind 10019 nutzap info event */
|
|
33
|
+
export function AddNutzapInfoMint(mint) {
|
|
34
|
+
return async function* ({ events, factory, self }) {
|
|
35
|
+
const mints = Array.isArray(mint) ? mint : [mint];
|
|
36
|
+
const operations = mints.map((m) => {
|
|
37
|
+
const tag = m.units ? ["mint", m.url, ...m.units] : ["mint", m.url];
|
|
38
|
+
return addNameValueTag(tag, false);
|
|
39
|
+
});
|
|
40
|
+
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
41
|
+
const draft = nutzapInfo
|
|
42
|
+
? await factory.modifyTags(nutzapInfo, ...operations)
|
|
43
|
+
: await factory.build({ kind: NUTZAP_INFO_KIND }, modifyPublicTags(...operations));
|
|
44
|
+
const signed = await factory.sign(draft);
|
|
45
|
+
yield signed;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** An action to remove a mint from the kind 10019 nutzap info event */
|
|
49
|
+
export function RemoveNutzapInfoMint(mint) {
|
|
50
|
+
return async function* ({ events, factory, self }) {
|
|
51
|
+
if (typeof mint === "string")
|
|
52
|
+
mint = [mint];
|
|
53
|
+
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
54
|
+
if (!nutzapInfo)
|
|
55
|
+
return;
|
|
56
|
+
const draft = await factory.modifyTags(nutzapInfo, ...mint.map((m) => removeNameValueTag(["mint", m])));
|
|
57
|
+
const signed = await factory.sign(draft);
|
|
58
|
+
yield signed;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** An action to set the pubkey for the kind 10019 nutzap info event */
|
|
62
|
+
export function SetNutzapInfoPubkey(pubkey) {
|
|
63
|
+
return async function* ({ events, factory, self }) {
|
|
64
|
+
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
65
|
+
const draft = nutzapInfo
|
|
66
|
+
? await factory.modifyTags(nutzapInfo, setSingletonTag(["pubkey", pubkey], true))
|
|
67
|
+
: await factory.build({ kind: NUTZAP_INFO_KIND }, modifyPublicTags(setSingletonTag(["pubkey", pubkey], true)));
|
|
68
|
+
const signed = await factory.sign(draft);
|
|
69
|
+
yield signed;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** An action to update the entire nutzap info event */
|
|
73
|
+
export function UpdateNutzapInfo(relays, mints, pubkey) {
|
|
74
|
+
return async function* ({ events, factory, self }) {
|
|
75
|
+
const operations = [setNutzapInfoRelays(relays), setNutzapInfoMints(mints), setNutzapInfoPubkey(pubkey)];
|
|
76
|
+
const nutzapInfo = events.getReplaceable(NUTZAP_INFO_KIND, self);
|
|
77
|
+
const draft = nutzapInfo
|
|
78
|
+
? await factory.modify(nutzapInfo, ...operations)
|
|
79
|
+
: await factory.build({ kind: NUTZAP_INFO_KIND }, ...operations);
|
|
80
|
+
const signed = await factory.sign(draft);
|
|
81
|
+
yield signed;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
import { Action } from "applesauce-actions";
|
|
3
|
+
import { NostrEvent } from "nostr-tools";
|
|
4
|
+
import { ProfilePointer } from "nostr-tools/nip19";
|
|
5
|
+
/** Creates a NIP-61 nutzap event for an event with a token */
|
|
6
|
+
export declare function NutzapEvent(event: NostrEvent, token: Token, comment?: string): Action;
|
|
7
|
+
/** Creates a NIP-61 nutzap event to a users profile */
|
|
8
|
+
export declare function NutzapProfile(user: string | ProfilePointer, token: Token, comment?: string): Action;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { NutzapBlueprint, ProfileNutzapBlueprint } from "../blueprints/zaps.js";
|
|
2
|
+
import { NUTZAP_INFO_KIND, verifyProofsLocked } from "../helpers/zap-info.js";
|
|
3
|
+
/** Creates a NIP-61 nutzap event for an event with a token */
|
|
4
|
+
export function NutzapEvent(event, token, comment) {
|
|
5
|
+
return async function* ({ events, factory }) {
|
|
6
|
+
const recipient = event.pubkey;
|
|
7
|
+
const info = events.getReplaceable(NUTZAP_INFO_KIND, recipient);
|
|
8
|
+
if (!info)
|
|
9
|
+
throw new Error("Nutzap info not found");
|
|
10
|
+
// Verify all tokens are p2pk locked
|
|
11
|
+
verifyProofsLocked(token.proofs, info);
|
|
12
|
+
// NOTE: Disabled because mints and units should be checked by the app before
|
|
13
|
+
// const mints = getNutzapInfoMints(info);
|
|
14
|
+
// if (!mints.some((m) => m.mint === token.mint)) throw new Error("Token mint not found in nutzap info");
|
|
15
|
+
const nutzap = await factory.sign(await factory.create(NutzapBlueprint, event, token, comment || token.memo));
|
|
16
|
+
yield nutzap;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Creates a NIP-61 nutzap event to a users profile */
|
|
20
|
+
export function NutzapProfile(user, token, comment) {
|
|
21
|
+
return async function* ({ events, factory }) {
|
|
22
|
+
const info = events.getReplaceable(NUTZAP_INFO_KIND, typeof user === "string" ? user : user.pubkey);
|
|
23
|
+
if (!info)
|
|
24
|
+
throw new Error("Nutzap info not found");
|
|
25
|
+
// Verify all tokens are p2pk locked
|
|
26
|
+
verifyProofsLocked(token.proofs, info);
|
|
27
|
+
const nutzap = await factory.sign(await factory.create(ProfileNutzapBlueprint, user, token, comment || token.memo));
|
|
28
|
+
yield nutzap;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { EventPointer } from "nostr-tools/nip19";
|
|
2
|
+
import { HistoryContent } from "../helpers/history.js";
|
|
3
|
+
/** A blueprint that creates a wallet history event */
|
|
4
|
+
export declare function WalletHistoryBlueprint(content: HistoryContent, redeemed: (string | EventPointer)[]): import("applesauce-factory").EventBlueprint;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { blueprint } from "applesauce-factory";
|
|
2
|
+
import { WALLET_HISTORY_KIND } from "../helpers/history.js";
|
|
3
|
+
import { setHistoryContent, setHistoryRedeemed } from "../operations/history.js";
|
|
4
|
+
/** A blueprint that creates a wallet history event */
|
|
5
|
+
export function WalletHistoryBlueprint(content, redeemed) {
|
|
6
|
+
return blueprint(WALLET_HISTORY_KIND,
|
|
7
|
+
// set the encrypted tags on the event
|
|
8
|
+
setHistoryContent(content),
|
|
9
|
+
// set the public redeemed tags
|
|
10
|
+
setHistoryRedeemed(redeemed));
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { blueprint } from "applesauce-factory";
|
|
2
|
+
import { setContent } from "applesauce-factory/operations/content";
|
|
3
|
+
import { includeSingletonTag } from "applesauce-factory/operations";
|
|
4
|
+
import { WALLET_INFO_KIND } from "../helpers/support.js";
|
|
5
|
+
/** Creates a wallet info event */
|
|
6
|
+
export function WalletInfoBlueprint(info) {
|
|
7
|
+
return blueprint(WALLET_INFO_KIND, setContent(info.methods.join(" ")), info.encryption ? includeSingletonTag(["encryption", info.encryption.join(" ")]) : undefined, info.notifications ? includeSingletonTag(["notifications", info.notifications.join(" ")]) : undefined);
|
|
8
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EventBlueprint } from "applesauce-factory";
|
|
2
|
+
import { WalletNotification } from "../helpers/notification.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a wallet notification event (kind 23197)
|
|
5
|
+
* @param client - The service pubkey
|
|
6
|
+
* @param notification - The notification to create an event for
|
|
7
|
+
* @param encryption - The encryption method to use (defaults to nip44_v2)
|
|
8
|
+
*/
|
|
9
|
+
export declare function WalletNotificationBlueprint(client: string, notification: WalletNotification): EventBlueprint;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a legacy wallet notification event (kind 23196)
|
|
12
|
+
* @param client - The service pubkey
|
|
13
|
+
* @param notification - The notification to create an event for
|
|
14
|
+
*/
|
|
15
|
+
export declare function WalletLegacyNotificationBlueprint(client: string, notification: WalletNotification): EventBlueprint;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { blueprint } from "applesauce-factory";
|
|
2
|
+
import { includeSingletonTag } from "applesauce-factory/operations";
|
|
3
|
+
import { setEncryptedContent } from "applesauce-factory/operations/content";
|
|
4
|
+
import { WALLET_NOTIFICATION_KIND, WALLET_LEGACY_NOTIFICATION_KIND, } from "../helpers/notification.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a wallet notification event (kind 23197)
|
|
7
|
+
* @param client - The service pubkey
|
|
8
|
+
* @param notification - The notification to create an event for
|
|
9
|
+
* @param encryption - The encryption method to use (defaults to nip44_v2)
|
|
10
|
+
*/
|
|
11
|
+
export function WalletNotificationBlueprint(client, notification) {
|
|
12
|
+
return blueprint(WALLET_NOTIFICATION_KIND, setEncryptedContent(client, JSON.stringify(notification)), includeSingletonTag(["p", client]));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a legacy wallet notification event (kind 23196)
|
|
16
|
+
* @param client - The service pubkey
|
|
17
|
+
* @param notification - The notification to create an event for
|
|
18
|
+
*/
|
|
19
|
+
export function WalletLegacyNotificationBlueprint(client, notification) {
|
|
20
|
+
return blueprint(WALLET_LEGACY_NOTIFICATION_KIND, setEncryptedContent(client, JSON.stringify(notification)), includeSingletonTag(["p", client]));
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EventBlueprint } from "applesauce-factory";
|
|
2
|
+
import { WalletRequest } from "../helpers/request.js";
|
|
3
|
+
import { WalletConnectEncryptionMethod } from "../helpers/encryption.js";
|
|
4
|
+
/**
|
|
5
|
+
* Creates a walelt request event
|
|
6
|
+
* @param service - The service pubkey
|
|
7
|
+
* @param request - The request to create an event for
|
|
8
|
+
*/
|
|
9
|
+
export declare function WalletRequestBlueprint<TRequest extends WalletRequest>(service: string, request: TRequest, encryption?: WalletConnectEncryptionMethod): EventBlueprint;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { blueprint } from "applesauce-factory";
|
|
2
|
+
import { includeSingletonTag } from "applesauce-factory/operations";
|
|
3
|
+
import { setEncryptedContent } from "applesauce-factory/operations/content";
|
|
4
|
+
import { WALLET_REQUEST_KIND } from "../helpers/request.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a walelt request event
|
|
7
|
+
* @param service - The service pubkey
|
|
8
|
+
* @param request - The request to create an event for
|
|
9
|
+
*/
|
|
10
|
+
export function WalletRequestBlueprint(service, request, encryption = "nip44_v2") {
|
|
11
|
+
return blueprint(WALLET_REQUEST_KIND, setEncryptedContent(service, JSON.stringify(request), encryption === "nip44_v2" ? "nip44" : "nip04"), includeSingletonTag(["p", service]), includeSingletonTag(["encryption", encryption]));
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EventBlueprint } from "applesauce-factory";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
3
|
+
import { WalletResponse } from "../helpers/response.js";
|
|
4
|
+
/** Creates a wallet response event */
|
|
5
|
+
export declare function WalletResponseBlueprint(request: NostrEvent, response: WalletResponse): EventBlueprint;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { blueprint } from "applesauce-factory";
|
|
2
|
+
import { includeSingletonTag } from "applesauce-factory/operations";
|
|
3
|
+
import { setEncryptedContent } from "applesauce-factory/operations/content";
|
|
4
|
+
import { getWalletRequestEncryption } from "../helpers/request.js";
|
|
5
|
+
import { WALLET_RESPONSE_KIND } from "../helpers/response.js";
|
|
6
|
+
/** Creates a wallet response event */
|
|
7
|
+
export function WalletResponseBlueprint(request, response) {
|
|
8
|
+
const encryption = getWalletRequestEncryption(request);
|
|
9
|
+
return blueprint(WALLET_RESPONSE_KIND, setEncryptedContent(request.pubkey, JSON.stringify(response), encryption === "nip44_v2" ? "nip44" : "nip04"), includeSingletonTag(["e", request.id]), includeSingletonTag(["p", request.pubkey]));
|
|
10
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { blueprint } from "applesauce-factory";
|
|
2
|
+
import { setContent } from "applesauce-factory/operations/content";
|
|
3
|
+
import { includeSingletonTag } from "applesauce-factory/operations";
|
|
4
|
+
import { WALLET_INFO_KIND } from "../helpers/support.js";
|
|
5
|
+
/** Creates a wallet info event */
|
|
6
|
+
export function WalletSupportBlueprint(info) {
|
|
7
|
+
return blueprint(WALLET_INFO_KIND, setContent(info.methods.join(" ")), info.encryption ? includeSingletonTag(["encryption", info.encryption.join(" ")]) : undefined, info.notifications ? includeSingletonTag(["notifications", info.notifications.join(" ")]) : undefined);
|
|
8
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Token } from "@cashu/cashu-ts";
|
|
2
|
+
/**
|
|
3
|
+
* A blueprint for a wallet token event, takes a cashu token and previous deleted token event ids
|
|
4
|
+
* @param token the cashu token to store
|
|
5
|
+
* @param [del=[]] an array of previous token event ids that are deleted
|
|
6
|
+
*/
|
|
7
|
+
export declare function WalletTokenBlueprint(token: Token, del?: string[]): import("applesauce-factory").EventBlueprint;
|