applesauce-wallet-connect 0.0.0-next-20250808173123 → 0.0.0-next-20250828144630
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/blueprints/support.d.ts +1 -1
- package/dist/blueprints/support.js +4 -2
- package/dist/helpers/auth-uri.d.ts +44 -0
- package/dist/helpers/auth-uri.js +113 -0
- package/dist/helpers/connect-uri.d.ts +5 -0
- package/dist/helpers/connect-uri.js +6 -2
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/interop.d.ts +35 -0
- package/dist/interop.js +19 -0
- package/dist/types.d.ts +5 -0
- package/dist/wallet-connect.d.ts +25 -14
- package/dist/wallet-connect.js +95 -34
- package/dist/wallet-service.d.ts +19 -10
- package/dist/wallet-service.js +40 -20
- package/package.json +4 -4
- package/dist/actions/index.d.ts +0 -1
- package/dist/actions/index.js +0 -1
- package/dist/actions/tokens.d.ts +0 -17
- package/dist/actions/tokens.js +0 -110
- package/dist/actions/wallet.d.ts +0 -13
- package/dist/actions/wallet.js +0 -64
- package/dist/actions/zap-info.d.ts +0 -22
- 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/blueprints/history.d.ts +0 -4
- package/dist/blueprints/history.js +0 -11
- package/dist/blueprints/info.d.ts +0 -4
- package/dist/blueprints/info.js +0 -8
- package/dist/blueprints/tokens.d.ts +0 -7
- package/dist/blueprints/tokens.js +0 -11
- package/dist/blueprints/wallet.d.ts +0 -5
- package/dist/blueprints/wallet.js +0 -11
- package/dist/blueprints/zaps.d.ts +0 -8
- package/dist/blueprints/zaps.js +0 -12
- package/dist/helpers/animated-qr.d.ts +0 -30
- package/dist/helpers/animated-qr.js +0 -71
- package/dist/helpers/history.d.ts +0 -26
- package/dist/helpers/history.js +0 -47
- package/dist/helpers/info.d.ts +0 -34
- package/dist/helpers/info.js +0 -97
- package/dist/helpers/methods.d.ts +0 -1
- package/dist/helpers/methods.js +0 -1
- package/dist/helpers/nutzap.d.ts +0 -27
- package/dist/helpers/nutzap.js +0 -66
- package/dist/helpers/tokens.d.ts +0 -58
- package/dist/helpers/tokens.js +0 -162
- package/dist/helpers/wallet.d.ts +0 -15
- package/dist/helpers/wallet.js +0 -41
- package/dist/helpers/zap-info.d.ts +0 -19
- package/dist/helpers/zap-info.js +0 -42
- package/dist/interface.d.ts +0 -6
- package/dist/interface.js +0 -1
- package/dist/models/history.d.ts +0 -6
- package/dist/models/history.js +0 -21
- package/dist/models/index.d.ts +0 -4
- package/dist/models/index.js +0 -4
- package/dist/models/nutzap.d.ts +0 -6
- package/dist/models/nutzap.js +0 -16
- package/dist/models/tokens.d.ts +0 -6
- package/dist/models/tokens.js +0 -58
- package/dist/models/wallet.d.ts +0 -13
- package/dist/models/wallet.js +0 -18
- package/dist/operations/history.d.ts +0 -7
- package/dist/operations/history.js +0 -34
- package/dist/operations/index.d.ts +0 -5
- package/dist/operations/index.js +0 -5
- package/dist/operations/nutzap.d.ts +0 -14
- package/dist/operations/nutzap.js +0 -33
- package/dist/operations/tokens.d.ts +0 -4
- package/dist/operations/tokens.js +0 -24
- package/dist/operations/wallet.d.ts +0 -8
- package/dist/operations/wallet.js +0 -30
- package/dist/operations/zap-info.d.ts +0 -10
- package/dist/operations/zap-info.js +0 -17
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Connect to a wallet service using a connection string:
|
|
|
24
24
|
```typescript
|
|
25
25
|
import { WalletConnect } from "applesauce-wallet-connect";
|
|
26
26
|
|
|
27
|
-
const wallet = WalletConnect.
|
|
27
|
+
const wallet = WalletConnect.fromConnectURI("nostr+walletconnect://relay.example.com?secret=...&pubkey=...");
|
|
28
28
|
|
|
29
29
|
// Pay an invoice
|
|
30
30
|
const result = await wallet.payInvoice("lnbc1...");
|
|
@@ -87,10 +87,10 @@ await service.start();
|
|
|
87
87
|
console.log("Wallet service started");
|
|
88
88
|
|
|
89
89
|
// Get the connection string for the wallet service
|
|
90
|
-
console.log(service.
|
|
90
|
+
console.log(service.getConnectURI());
|
|
91
91
|
|
|
92
92
|
// Stop the service when done
|
|
93
|
-
|
|
93
|
+
service.stop();
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
## Supported Methods
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { EventBlueprint } from "applesauce-factory";
|
|
2
2
|
import { WalletSupport } from "../helpers/support.js";
|
|
3
3
|
/** Creates a wallet info event */
|
|
4
|
-
export declare function WalletSupportBlueprint(info: WalletSupport): EventBlueprint;
|
|
4
|
+
export declare function WalletSupportBlueprint(info: WalletSupport, client?: string): EventBlueprint;
|
|
@@ -3,6 +3,8 @@ import { setContent } from "applesauce-factory/operations/content";
|
|
|
3
3
|
import { includeSingletonTag } from "applesauce-factory/operations";
|
|
4
4
|
import { WALLET_INFO_KIND } from "../helpers/support.js";
|
|
5
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
|
|
6
|
+
export function WalletSupportBlueprint(info, client) {
|
|
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
|
+
// An optional client pubkey to notify the service is created (used for nostr+walletauth URI connections)
|
|
9
|
+
client ? includeSingletonTag(["p", client]) : undefined);
|
|
8
10
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { NotificationType } from "./notification.js";
|
|
2
|
+
import { WalletMethod } from "./support.js";
|
|
3
|
+
export interface WalletAuthURI {
|
|
4
|
+
/** The public key of the client requesting authorization */
|
|
5
|
+
client: string;
|
|
6
|
+
/** Required. URL of the relay where the client intends to communicate with the wallet service */
|
|
7
|
+
relays: string[];
|
|
8
|
+
/** The name of the client app (optional) */
|
|
9
|
+
name?: string;
|
|
10
|
+
/** The URL of an icon of the client app to display on the confirmation page (optional) */
|
|
11
|
+
icon?: string;
|
|
12
|
+
/** URI to open after the connection is created (optional) */
|
|
13
|
+
returnTo?: string;
|
|
14
|
+
/** The connection cannot be used after this date. Unix timestamp in seconds (optional) */
|
|
15
|
+
expiresAt?: number;
|
|
16
|
+
/** The maximum amount in millisats that can be sent per renewal period (optional) */
|
|
17
|
+
maxAmount?: number;
|
|
18
|
+
/** The reset the budget at the end of the given budget renewal. Can be never (default), daily, weekly, monthly, yearly (optional) */
|
|
19
|
+
budgetRenewal?: "never" | "daily" | "weekly" | "monthly" | "yearly";
|
|
20
|
+
/** List of request types that you need permission for (optional) */
|
|
21
|
+
methods?: WalletMethod[];
|
|
22
|
+
/** List of notification types that you need permission for (optional) */
|
|
23
|
+
notifications?: NotificationType[];
|
|
24
|
+
/** The makes an isolated app connection / sub-wallet with its own balance and only access to its own transaction list (optional) */
|
|
25
|
+
isolated?: boolean;
|
|
26
|
+
/** Url encoded, JSON-serialized metadata that describes the app connection (optional) */
|
|
27
|
+
metadata?: Record<string, any>;
|
|
28
|
+
/** The wallet name for nostr+walletauth+walletname scheme (optional) */
|
|
29
|
+
walletName?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parses a nostr+walletauth URI
|
|
33
|
+
* @throws {Error} if the authorization URI is invalid
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseWalletAuthURI(authURI: string): WalletAuthURI;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a nostr+walletauth URI from a WalletAuthURI object
|
|
38
|
+
*/
|
|
39
|
+
export declare function createWalletAuthURI(parts: WalletAuthURI): string;
|
|
40
|
+
/**
|
|
41
|
+
* Validates a WalletAuthURI object
|
|
42
|
+
* @returns true if valid, throws Error if invalid
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateWalletAuthURI(parts: WalletAuthURI): boolean;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { mergeRelaySets } from "applesauce-core/helpers";
|
|
2
|
+
/**
|
|
3
|
+
* Parses a nostr+walletauth URI
|
|
4
|
+
* @throws {Error} if the authorization URI is invalid
|
|
5
|
+
*/
|
|
6
|
+
export function parseWalletAuthURI(authURI) {
|
|
7
|
+
const { host, pathname, searchParams, protocol } = new URL(authURI);
|
|
8
|
+
// Check if it's a valid wallet auth protocol
|
|
9
|
+
if (!protocol.startsWith("nostr+walletauth")) {
|
|
10
|
+
throw new Error("invalid wallet auth uri protocol");
|
|
11
|
+
}
|
|
12
|
+
// Extract wallet name if present (nostr+walletauth+walletname://)
|
|
13
|
+
const walletName = protocol.includes("+") && protocol.split("+").length > 2 ? protocol.split("+")[2]?.replace(/:$/, "") : undefined;
|
|
14
|
+
// The client pubkey is in the pathname or host
|
|
15
|
+
const client = pathname || host;
|
|
16
|
+
if (!client)
|
|
17
|
+
throw new Error("missing client public key in authorization URI");
|
|
18
|
+
// Relay is required
|
|
19
|
+
const relays = mergeRelaySets(searchParams.getAll("relay"));
|
|
20
|
+
if (relays.length === 0)
|
|
21
|
+
throw new Error("missing required relay parameter in authorization URI");
|
|
22
|
+
// Parse optional parameters
|
|
23
|
+
const name = searchParams.get("name") ?? undefined;
|
|
24
|
+
const icon = searchParams.get("icon") ?? undefined;
|
|
25
|
+
const returnTo = searchParams.get("return_to") ?? undefined;
|
|
26
|
+
const expiresAtParam = searchParams.get("expires_at");
|
|
27
|
+
const expiresAt = expiresAtParam ? parseInt(expiresAtParam, 10) : undefined;
|
|
28
|
+
const maxAmountParam = searchParams.get("max_amount");
|
|
29
|
+
const maxAmount = maxAmountParam ? parseInt(maxAmountParam, 10) : undefined;
|
|
30
|
+
const budgetRenewal = searchParams.get("budget_renewal");
|
|
31
|
+
const methodsParam = searchParams.get("request_methods");
|
|
32
|
+
const methods = methodsParam ? methodsParam.split(" ") : undefined;
|
|
33
|
+
const notificationsParam = searchParams.get("notification_types");
|
|
34
|
+
const notifications = notificationsParam ? notificationsParam.split(" ") : undefined;
|
|
35
|
+
const isolatedParam = searchParams.get("isolated");
|
|
36
|
+
const isolated = isolatedParam ? isolatedParam === "true" : undefined;
|
|
37
|
+
const metadataParam = searchParams.get("metadata");
|
|
38
|
+
let metadata;
|
|
39
|
+
if (metadataParam) {
|
|
40
|
+
try {
|
|
41
|
+
metadata = JSON.parse(decodeURIComponent(metadataParam));
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw new Error("invalid metadata parameter in authorization URI");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
client,
|
|
49
|
+
relays,
|
|
50
|
+
name,
|
|
51
|
+
icon,
|
|
52
|
+
returnTo,
|
|
53
|
+
expiresAt,
|
|
54
|
+
maxAmount,
|
|
55
|
+
budgetRenewal: budgetRenewal || undefined,
|
|
56
|
+
methods,
|
|
57
|
+
notifications,
|
|
58
|
+
isolated,
|
|
59
|
+
metadata,
|
|
60
|
+
walletName,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates a nostr+walletauth URI from a WalletAuthURI object
|
|
65
|
+
*/
|
|
66
|
+
export function createWalletAuthURI(parts) {
|
|
67
|
+
validateWalletAuthURI(parts);
|
|
68
|
+
// Determine the protocol based on whether wallet name is specified
|
|
69
|
+
const protocol = parts.walletName ? `nostr+walletauth+${parts.walletName}` : "nostr+walletauth";
|
|
70
|
+
const url = new URL(`${protocol}://${parts.client}`);
|
|
71
|
+
// Add required relay parameter
|
|
72
|
+
for (const relay of parts.relays)
|
|
73
|
+
url.searchParams.append("relay", relay);
|
|
74
|
+
// Add optional parameters
|
|
75
|
+
if (parts.name)
|
|
76
|
+
url.searchParams.append("name", parts.name);
|
|
77
|
+
if (parts.icon)
|
|
78
|
+
url.searchParams.append("icon", parts.icon);
|
|
79
|
+
if (parts.returnTo)
|
|
80
|
+
url.searchParams.append("return_to", parts.returnTo);
|
|
81
|
+
if (parts.expiresAt)
|
|
82
|
+
url.searchParams.append("expires_at", parts.expiresAt.toString());
|
|
83
|
+
if (parts.maxAmount)
|
|
84
|
+
url.searchParams.append("max_amount", parts.maxAmount.toString());
|
|
85
|
+
if (parts.budgetRenewal && parts.budgetRenewal !== "never")
|
|
86
|
+
url.searchParams.append("budget_renewal", parts.budgetRenewal);
|
|
87
|
+
if (parts.methods && parts.methods.length > 0)
|
|
88
|
+
url.searchParams.append("request_methods", parts.methods.join(" "));
|
|
89
|
+
if (parts.notifications && parts.notifications.length > 0)
|
|
90
|
+
url.searchParams.append("notification_types", parts.notifications.join(" "));
|
|
91
|
+
if (parts.isolated !== undefined)
|
|
92
|
+
url.searchParams.append("isolated", parts.isolated.toString());
|
|
93
|
+
if (parts.metadata)
|
|
94
|
+
url.searchParams.append("metadata", encodeURIComponent(JSON.stringify(parts.metadata)));
|
|
95
|
+
return url.toString();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Validates a WalletAuthURI object
|
|
99
|
+
* @returns true if valid, throws Error if invalid
|
|
100
|
+
*/
|
|
101
|
+
export function validateWalletAuthURI(parts) {
|
|
102
|
+
if (!parts.client || parts.client.length === 0)
|
|
103
|
+
throw new Error("client public key is required");
|
|
104
|
+
if (!parts.relays || parts.relays.length === 0)
|
|
105
|
+
throw new Error("at least one relay is required");
|
|
106
|
+
if (parts.expiresAt && parts.expiresAt <= Math.floor(Date.now() / 1000))
|
|
107
|
+
throw new Error("expires_at must be in the future");
|
|
108
|
+
if (parts.maxAmount && parts.maxAmount <= 0)
|
|
109
|
+
throw new Error("max_amount must be positive");
|
|
110
|
+
if (parts.budgetRenewal && !["never", "daily", "weekly", "monthly", "yearly"].includes(parts.budgetRenewal))
|
|
111
|
+
throw new Error("invalid budget_renewal value");
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export interface WalletConnectURI {
|
|
2
|
+
/** The pubkey of the wallet service */
|
|
2
3
|
service: string;
|
|
4
|
+
/** The relays to use for the connection */
|
|
3
5
|
relays: string[];
|
|
6
|
+
/** The secret key that the client will use to encrypt messages */
|
|
4
7
|
secret: string;
|
|
8
|
+
/** An optional lub16 lightning address that is associated with the wallet */
|
|
9
|
+
lud16?: string;
|
|
5
10
|
}
|
|
6
11
|
/**
|
|
7
12
|
* Parses a nostr+walletconnect URI
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { mergeRelaySets } from "applesauce-core/helpers";
|
|
1
2
|
/**
|
|
2
3
|
* Parses a nostr+walletconnect URI
|
|
3
4
|
* @throws {Error} if the connection string is invalid
|
|
@@ -7,11 +8,12 @@ export function parseWalletConnectURI(connectionString) {
|
|
|
7
8
|
if (protocol !== "nostr+walletconnect:")
|
|
8
9
|
throw new Error("invalid wallet connect uri protocol");
|
|
9
10
|
const service = pathname || host;
|
|
10
|
-
const relays = searchParams.getAll("relay");
|
|
11
|
+
const relays = mergeRelaySets(searchParams.getAll("relay"));
|
|
11
12
|
const secret = searchParams.get("secret");
|
|
13
|
+
const lud16 = searchParams.get("lud16") ?? undefined;
|
|
12
14
|
if (!service || relays.length === 0 || !secret)
|
|
13
15
|
throw new Error("invalid connection string");
|
|
14
|
-
return { service, relays, secret };
|
|
16
|
+
return { service, relays, secret, lud16 };
|
|
15
17
|
}
|
|
16
18
|
/** Creates a nostr+walletconnect URI from a WalletConnectURI object */
|
|
17
19
|
export function createWalletConnectURI(parts) {
|
|
@@ -19,5 +21,7 @@ export function createWalletConnectURI(parts) {
|
|
|
19
21
|
for (const relay of parts.relays)
|
|
20
22
|
url.searchParams.append("relay", relay);
|
|
21
23
|
url.searchParams.append("secret", parts.secret);
|
|
24
|
+
if (parts.lud16)
|
|
25
|
+
url.searchParams.append("lud16", parts.lud16);
|
|
22
26
|
return url.toString();
|
|
23
27
|
}
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/index.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Filter, NostrEvent } from "nostr-tools";
|
|
2
|
+
import { ObservableInput } from "rxjs";
|
|
3
|
+
/** A method used to subscribe to events on a set of relays */
|
|
4
|
+
export type NostrSubscriptionMethod = (relays: string[], filters: Filter[]) => ObservableInput<NostrEvent | string>;
|
|
5
|
+
/** A method used for publishing an event, can return a Promise that completes when published or an Observable that completes when published*/
|
|
6
|
+
export type NostrPublishMethod = (relays: string[], event: NostrEvent) => Promise<any> | ObservableInput<any>;
|
|
7
|
+
/** A simple pool type that combines the subscription and publish methods */
|
|
8
|
+
export type NostrPool = {
|
|
9
|
+
subscription: NostrSubscriptionMethod;
|
|
10
|
+
publish: NostrPublishMethod;
|
|
11
|
+
};
|
|
12
|
+
/** Options for setting the subscription and publish methods */
|
|
13
|
+
export type NostrConnectionMethodsOptions = {
|
|
14
|
+
/** An optional method for subscribing to relays */
|
|
15
|
+
subscriptionMethod?: NostrSubscriptionMethod;
|
|
16
|
+
/** An optional method for publishing events */
|
|
17
|
+
publishMethod?: NostrPublishMethod;
|
|
18
|
+
/** An optional pool for connection methods */
|
|
19
|
+
pool?: NostrPool;
|
|
20
|
+
};
|
|
21
|
+
/** A class that implements has global fallback methods for subscription and publish methods */
|
|
22
|
+
export interface NostrConnectionClassMethods {
|
|
23
|
+
new (...args: any[]): any;
|
|
24
|
+
/** A fallback method to use for subscriptionMethod if none is passed in when creating the client */
|
|
25
|
+
subscriptionMethod: NostrSubscriptionMethod | undefined;
|
|
26
|
+
/** A fallback method to use for publishMethod if none is passed in when creating the client */
|
|
27
|
+
publishMethod: NostrPublishMethod | undefined;
|
|
28
|
+
/** A fallback pool to use if none is pass in when creating the signer */
|
|
29
|
+
pool: NostrPool | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Get the subscription and publish methods for a NostrConnect class */
|
|
32
|
+
export declare function getConnectionMethods(options: NostrConnectionMethodsOptions, cls?: NostrConnectionClassMethods): {
|
|
33
|
+
subscriptionMethod: NostrSubscriptionMethod;
|
|
34
|
+
publishMethod: NostrPublishMethod;
|
|
35
|
+
};
|
package/dist/interop.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Get the subscription and publish methods for a NostrConnect class */
|
|
2
|
+
export function getConnectionMethods(options, cls) {
|
|
3
|
+
const subscriptionMethod = options.subscriptionMethod ||
|
|
4
|
+
options.pool?.subscription.bind(options.pool) ||
|
|
5
|
+
cls?.subscriptionMethod ||
|
|
6
|
+
cls?.pool?.subscription.bind(cls.pool);
|
|
7
|
+
if (!subscriptionMethod)
|
|
8
|
+
throw new Error("Missing subscriptionMethod, either pass a method or set subscriptionMethod globally on the class");
|
|
9
|
+
const publishMethod = options.publishMethod ||
|
|
10
|
+
options.pool?.publish.bind(options.pool) ||
|
|
11
|
+
cls?.publishMethod ||
|
|
12
|
+
cls?.pool?.publish.bind(cls.pool);
|
|
13
|
+
if (!publishMethod)
|
|
14
|
+
throw new Error("Missing publishMethod, either pass a method or set publishMethod globally on the class");
|
|
15
|
+
return {
|
|
16
|
+
subscriptionMethod,
|
|
17
|
+
publishMethod,
|
|
18
|
+
};
|
|
19
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -4,3 +4,8 @@ import { Observable } from "rxjs";
|
|
|
4
4
|
export type NostrSubscriptionMethod = (relays: string[], filters: Filter[]) => Observable<NostrEvent | string>;
|
|
5
5
|
/** A method used for publishing an event, can return a Promise that completes when published or an Observable that completes when published*/
|
|
6
6
|
export type NostrPublishMethod = (relays: string[], event: NostrEvent) => Promise<any> | Observable<any>;
|
|
7
|
+
/** A simple pool type that combines the subscription and publish methods */
|
|
8
|
+
export type NostrPool = {
|
|
9
|
+
subscription: NostrSubscriptionMethod;
|
|
10
|
+
publish: NostrPublishMethod;
|
|
11
|
+
};
|
package/dist/wallet-connect.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { EncryptionMethod } from "applesauce-core/helpers";
|
|
1
2
|
import { EventSigner } from "applesauce-factory";
|
|
2
3
|
import { NostrEvent } from "nostr-tools";
|
|
3
|
-
import { Observable, Subscription } from "rxjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { NostrPublishMethod, NostrSubscriptionMethod } from "./types.js";
|
|
4
|
+
import { BehaviorSubject, Observable, Subscription } from "rxjs";
|
|
5
|
+
import { GetBalanceResult, GetInfoResult, ListTransactionsResult, LookupInvoiceResult, MakeInvoiceParams, MakeInvoiceResult, NotificationType, PayInvoiceResult, PayKeysendResult, WalletAuthURI, WalletConnectEncryptionMethod, WalletConnectURI, WalletMethod, WalletNotification, WalletRequest, WalletResponse, WalletSupport } from "./helpers/index.js";
|
|
6
|
+
import { NostrConnectionMethodsOptions, NostrPool, NostrPublishMethod, NostrSubscriptionMethod } from "./interop.js";
|
|
7
7
|
export type SerializedWalletConnect = WalletConnectURI;
|
|
8
|
-
export type WalletConnectOptions = {
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
|
|
8
|
+
export type WalletConnectOptions = NostrConnectionMethodsOptions & {
|
|
9
|
+
/** The secret to use for the connection */
|
|
10
|
+
secret: Uint8Array;
|
|
11
|
+
/** The relays to use for the connection */
|
|
12
|
+
relays: string[];
|
|
13
|
+
/** The service pubkey to use for the connection (optional) */
|
|
14
|
+
service?: string;
|
|
13
15
|
/** Default timeout for RPC requests in milliseconds */
|
|
14
16
|
timeout?: number;
|
|
15
17
|
};
|
|
@@ -18,6 +20,8 @@ export declare class WalletConnect {
|
|
|
18
20
|
static subscriptionMethod: NostrSubscriptionMethod | undefined;
|
|
19
21
|
/** A fallback method to use for publishMethod if none is passed in when creating the client */
|
|
20
22
|
static publishMethod: NostrPublishMethod | undefined;
|
|
23
|
+
/** A fallback pool to use if none is pass in when creating the signer */
|
|
24
|
+
static pool: NostrPool | undefined;
|
|
21
25
|
/** A method that is called when an event needs to be published */
|
|
22
26
|
protected publishMethod: NostrPublishMethod;
|
|
23
27
|
/** The active nostr subscription method */
|
|
@@ -27,8 +31,9 @@ export declare class WalletConnect {
|
|
|
27
31
|
protected readonly signer: EventSigner;
|
|
28
32
|
/** The relays to use for the connection */
|
|
29
33
|
readonly relays: string[];
|
|
30
|
-
/** The wallet service public key */
|
|
31
|
-
|
|
34
|
+
/** The wallet service public key ( unset if waiting for service ) */
|
|
35
|
+
service$: BehaviorSubject<string | undefined>;
|
|
36
|
+
get service(): string | undefined;
|
|
32
37
|
/** Default timeout for requests */
|
|
33
38
|
defaultTimeout: number;
|
|
34
39
|
/** Observable for wallet info updates */
|
|
@@ -39,7 +44,9 @@ export declare class WalletConnect {
|
|
|
39
44
|
protected events$: Observable<NostrEvent>;
|
|
40
45
|
/** Shared observable for all wallet notifications */
|
|
41
46
|
notifications$: Observable<WalletNotification>;
|
|
42
|
-
|
|
47
|
+
/** An internal observable for listening for the wallet service to connect */
|
|
48
|
+
protected waitForService$: Observable<string>;
|
|
49
|
+
constructor(options: WalletConnectOptions);
|
|
43
50
|
/** Process response events and return WalletResponse or throw error */
|
|
44
51
|
protected handleResponseEvent(event: NostrEvent, encryption?: EncryptionMethod): Promise<WalletResponse>;
|
|
45
52
|
/** Handle notification events */
|
|
@@ -53,6 +60,10 @@ export declare class WalletConnect {
|
|
|
53
60
|
* @returns a method to unsubscribe the listener
|
|
54
61
|
*/
|
|
55
62
|
notification<T extends WalletNotification>(type: T["notification_type"], listener: (notification: T["notification"]) => any): Subscription;
|
|
63
|
+
/** Gets the nostr+walletauth URI for the connection */
|
|
64
|
+
getAuthURI(parts?: Omit<WalletAuthURI, "client" | "relays">): string;
|
|
65
|
+
/** Wait for the wallet service to connect */
|
|
66
|
+
waitForService(abortSignal?: AbortSignal): Promise<string>;
|
|
56
67
|
/** Get the wallet support info */
|
|
57
68
|
getSupport(): Promise<WalletSupport | null>;
|
|
58
69
|
/** Check if the wallet supports a method */
|
|
@@ -105,7 +116,7 @@ export declare class WalletConnect {
|
|
|
105
116
|
/** Serialize the WalletConnect instance */
|
|
106
117
|
toJSON(): SerializedWalletConnect;
|
|
107
118
|
/** Create a new WalletConnect instance from a serialized object */
|
|
108
|
-
static fromJSON(json: SerializedWalletConnect, options?: WalletConnectOptions): WalletConnect;
|
|
119
|
+
static fromJSON(json: SerializedWalletConnect, options?: Omit<WalletConnectOptions, "secret" | "relays" | "service">): WalletConnect;
|
|
109
120
|
/** Create a new WalletConnect instance from a connection string */
|
|
110
|
-
static
|
|
121
|
+
static fromConnectURI(connectionString: string, options?: Omit<WalletConnectOptions, "secret" | "relays" | "service">): WalletConnect;
|
|
111
122
|
}
|
package/dist/wallet-connect.js
CHANGED
|
@@ -2,15 +2,18 @@ import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
|
|
|
2
2
|
import { simpleTimeout } from "applesauce-core";
|
|
3
3
|
import { create } from "applesauce-factory";
|
|
4
4
|
import { finalizeEvent, getPublicKey, nip04, nip44, verifyEvent } from "nostr-tools";
|
|
5
|
-
import { defer, filter, firstValueFrom, from, ignoreElements, lastValueFrom, map, merge, mergeMap, ReplaySubject, share, switchMap, timer, toArray, } from "rxjs";
|
|
5
|
+
import { BehaviorSubject, defer, filter, firstValueFrom, from, fromEvent, identity, ignoreElements, lastValueFrom, map, merge, mergeMap, repeat, ReplaySubject, retry, share, switchMap, takeUntil, tap, timer, toArray, } from "rxjs";
|
|
6
6
|
import { WalletRequestBlueprint } from "./blueprints/index.js";
|
|
7
7
|
import { createWalletError } from "./helpers/error.js";
|
|
8
|
-
import { getPreferredEncryption, getWalletNotification, getWalletRequestEncryption, getWalletResponse, getWalletResponseRequestId, getWalletSupport, isWalletNotificationLocked, isWalletResponseLocked, parseWalletConnectURI, supportsMethod, supportsNotifications, supportsNotificationType, unlockWalletNotification, unlockWalletResponse, WALLET_INFO_KIND, WALLET_LEGACY_NOTIFICATION_KIND, WALLET_NOTIFICATION_KIND, WALLET_RESPONSE_KIND, } from "./helpers/index.js";
|
|
8
|
+
import { createWalletAuthURI, getPreferredEncryption, getWalletNotification, getWalletRequestEncryption, getWalletResponse, getWalletResponseRequestId, getWalletSupport, isWalletNotificationLocked, isWalletResponseLocked, parseWalletConnectURI, supportsMethod, supportsNotifications, supportsNotificationType, unlockWalletNotification, unlockWalletResponse, WALLET_INFO_KIND, WALLET_LEGACY_NOTIFICATION_KIND, WALLET_NOTIFICATION_KIND, WALLET_RESPONSE_KIND, } from "./helpers/index.js";
|
|
9
|
+
import { getConnectionMethods, } from "./interop.js";
|
|
9
10
|
export class WalletConnect {
|
|
10
11
|
/** A fallback method to use for subscriptionMethod if none is passed in when creating the client */
|
|
11
12
|
static subscriptionMethod = undefined;
|
|
12
13
|
/** A fallback method to use for publishMethod if none is passed in when creating the client */
|
|
13
14
|
static publishMethod = undefined;
|
|
15
|
+
/** A fallback pool to use if none is pass in when creating the signer */
|
|
16
|
+
static pool = undefined;
|
|
14
17
|
/** A method that is called when an event needs to be published */
|
|
15
18
|
publishMethod;
|
|
16
19
|
/** The active nostr subscription method */
|
|
@@ -20,8 +23,11 @@ export class WalletConnect {
|
|
|
20
23
|
signer;
|
|
21
24
|
/** The relays to use for the connection */
|
|
22
25
|
relays;
|
|
23
|
-
/** The wallet service public key */
|
|
24
|
-
service;
|
|
26
|
+
/** The wallet service public key ( unset if waiting for service ) */
|
|
27
|
+
service$ = new BehaviorSubject(undefined);
|
|
28
|
+
get service() {
|
|
29
|
+
return this.service$.value;
|
|
30
|
+
}
|
|
25
31
|
/** Default timeout for requests */
|
|
26
32
|
defaultTimeout;
|
|
27
33
|
/** Observable for wallet info updates */
|
|
@@ -32,10 +38,12 @@ export class WalletConnect {
|
|
|
32
38
|
events$;
|
|
33
39
|
/** Shared observable for all wallet notifications */
|
|
34
40
|
notifications$;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
this.
|
|
41
|
+
/** An internal observable for listening for the wallet service to connect */
|
|
42
|
+
waitForService$;
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.secret = options.secret;
|
|
45
|
+
this.relays = options.relays;
|
|
46
|
+
this.service$.next(options.service);
|
|
39
47
|
this.defaultTimeout = options.timeout || 30000; // 30 second default timeout
|
|
40
48
|
// Create a signer for the factory
|
|
41
49
|
this.signer = {
|
|
@@ -50,29 +58,42 @@ export class WalletConnect {
|
|
|
50
58
|
decrypt: async (pubkey, ciphertext) => nip44.decrypt(ciphertext, nip44.getConversationKey(this.secret, pubkey)),
|
|
51
59
|
},
|
|
52
60
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
61
|
+
// Get the subscription and publish methods
|
|
62
|
+
const { subscriptionMethod, publishMethod } = getConnectionMethods(options, WalletConnect);
|
|
63
|
+
// Use arrow functions so "this" isn't bound to the signer
|
|
64
|
+
this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
|
|
65
|
+
this.publishMethod = (relays, event) => publishMethod(relays, event);
|
|
66
|
+
// Create shared observable for all wallet events
|
|
67
|
+
this.events$ = this.service$.pipe(switchMap((service) => {
|
|
68
|
+
const client = getPublicKey(this.secret);
|
|
69
|
+
// If the service is not known yet, subscribe to a wallet info event tagging the client
|
|
70
|
+
if (!service)
|
|
71
|
+
return from(this.subscriptionMethod(this.relays, [{ kinds: [WALLET_INFO_KIND], "#p": [client] }])).pipe(
|
|
72
|
+
// Keep the connection open indefinitely
|
|
73
|
+
repeat(),
|
|
74
|
+
// Retry on connection failure
|
|
75
|
+
retry(),
|
|
76
|
+
// Ignore strings (support for applesauce-relay)
|
|
77
|
+
filter((event) => typeof event !== "string"));
|
|
78
|
+
return from(this.subscriptionMethod(this.relays, [
|
|
79
|
+
// Subscribe to response events
|
|
80
|
+
{
|
|
81
|
+
kinds: [WALLET_RESPONSE_KIND, WALLET_NOTIFICATION_KIND, WALLET_LEGACY_NOTIFICATION_KIND],
|
|
82
|
+
"#p": [client],
|
|
83
|
+
authors: [service],
|
|
84
|
+
},
|
|
85
|
+
// Subscribe to wallet info events
|
|
86
|
+
{ kinds: [WALLET_INFO_KIND], authors: [service] },
|
|
87
|
+
])).pipe(
|
|
88
|
+
// Keep the connection open indefinitely
|
|
89
|
+
repeat(),
|
|
90
|
+
// Retry on connection failure
|
|
91
|
+
retry(),
|
|
92
|
+
// Ignore strings (support for applesauce-relay)
|
|
93
|
+
filter((event) => typeof event !== "string"),
|
|
94
|
+
// Only include events from the wallet service
|
|
95
|
+
filter((event) => event.pubkey === service));
|
|
96
|
+
}),
|
|
76
97
|
// Only create a single subscription to the relays
|
|
77
98
|
share({
|
|
78
99
|
resetOnRefCountZero: () => timer(60000), // Keep subscription open for 1 minute after last unsubscribe
|
|
@@ -83,6 +104,20 @@ export class WalletConnect {
|
|
|
83
104
|
}));
|
|
84
105
|
this.encryption$ = this.support$.pipe(map((info) => (info ? getPreferredEncryption(info) : "nip04")));
|
|
85
106
|
this.notifications$ = this.events$.pipe(filter((event) => event.kind === WALLET_NOTIFICATION_KIND), mergeMap((event) => this.handleNotificationEvent(event)));
|
|
107
|
+
this.waitForService$ = this.events$.pipe(
|
|
108
|
+
// Complete when the service is set
|
|
109
|
+
takeUntil(this.service$),
|
|
110
|
+
// Only listen for wallet info events
|
|
111
|
+
filter((event) => event.kind === WALLET_INFO_KIND && !this.service),
|
|
112
|
+
// Set the service to the pubkey of the wallet info event
|
|
113
|
+
tap((event) => {
|
|
114
|
+
// Set the service to the pubkey of the wallet info event
|
|
115
|
+
this.service$.next(event.pubkey);
|
|
116
|
+
}),
|
|
117
|
+
// Get the service pubkey from the event
|
|
118
|
+
map((event) => event.pubkey),
|
|
119
|
+
// Only create a single subscription to avoid multiple side effects
|
|
120
|
+
share());
|
|
86
121
|
}
|
|
87
122
|
/** Process response events and return WalletResponse or throw error */
|
|
88
123
|
async handleResponseEvent(event, encryption) {
|
|
@@ -115,6 +150,8 @@ export class WalletConnect {
|
|
|
115
150
|
}
|
|
116
151
|
/** Core RPC method that makes a request and returns the response */
|
|
117
152
|
request(request, options = {}) {
|
|
153
|
+
if (!this.service)
|
|
154
|
+
throw new Error("WalletConnect is not connected to a service");
|
|
118
155
|
// Create the request evnet
|
|
119
156
|
return defer(async () => {
|
|
120
157
|
// Get the preferred encryption method for the wallet
|
|
@@ -146,6 +183,18 @@ export class WalletConnect {
|
|
|
146
183
|
listener(notification.notification);
|
|
147
184
|
});
|
|
148
185
|
}
|
|
186
|
+
/** Gets the nostr+walletauth URI for the connection */
|
|
187
|
+
getAuthURI(parts) {
|
|
188
|
+
return createWalletAuthURI({ ...parts, client: getPublicKey(this.secret), relays: this.relays });
|
|
189
|
+
}
|
|
190
|
+
/** Wait for the wallet service to connect */
|
|
191
|
+
async waitForService(abortSignal) {
|
|
192
|
+
if (this.service)
|
|
193
|
+
return this.service;
|
|
194
|
+
return await firstValueFrom(this.waitForService$.pipe(
|
|
195
|
+
// Listen for abort signal
|
|
196
|
+
abortSignal ? takeUntil(fromEvent(abortSignal, "abort")) : identity));
|
|
197
|
+
}
|
|
149
198
|
// Convenience methods that return promises for easy API usage
|
|
150
199
|
/** Get the wallet support info */
|
|
151
200
|
getSupport() {
|
|
@@ -253,6 +302,8 @@ export class WalletConnect {
|
|
|
253
302
|
}
|
|
254
303
|
/** Serialize the WalletConnect instance */
|
|
255
304
|
toJSON() {
|
|
305
|
+
if (!this.service)
|
|
306
|
+
throw new Error("WalletConnect is not connected to a service");
|
|
256
307
|
return {
|
|
257
308
|
secret: bytesToHex(this.secret),
|
|
258
309
|
service: this.service,
|
|
@@ -261,11 +312,21 @@ export class WalletConnect {
|
|
|
261
312
|
}
|
|
262
313
|
/** Create a new WalletConnect instance from a serialized object */
|
|
263
314
|
static fromJSON(json, options) {
|
|
264
|
-
return new WalletConnect(
|
|
315
|
+
return new WalletConnect({
|
|
316
|
+
...options,
|
|
317
|
+
secret: hexToBytes(json.secret),
|
|
318
|
+
service: json.service,
|
|
319
|
+
relays: json.relays,
|
|
320
|
+
});
|
|
265
321
|
}
|
|
266
322
|
/** Create a new WalletConnect instance from a connection string */
|
|
267
|
-
static
|
|
323
|
+
static fromConnectURI(connectionString, options) {
|
|
268
324
|
const { secret, service, relays } = parseWalletConnectURI(connectionString);
|
|
269
|
-
return new WalletConnect(
|
|
325
|
+
return new WalletConnect({
|
|
326
|
+
...options,
|
|
327
|
+
secret: hexToBytes(secret),
|
|
328
|
+
service,
|
|
329
|
+
relays,
|
|
330
|
+
});
|
|
270
331
|
}
|
|
271
332
|
}
|