111 lines
3.1 KiB
TypeScript
111 lines
3.1 KiB
TypeScript
// Copyright (C) 2026 Whiterun LLC,
|
|
// This software is licensed under the GNU Lesser General Public License (LGPL), version 3.0 or later.
|
|
// A copy of the license can be found in the LICENSE file or at https://www.gnu.org/licenses/lgpl-3.0.html
|
|
|
|
import { RelayClient } from "./relay-client.js";
|
|
import {
|
|
RelayUpdatePayload,
|
|
RelayStatusCallback,
|
|
initiateRelay,
|
|
} from "./relay-handler.js";
|
|
import { decodeKeyExchangeURI, DEFAULT_RELAY_PORT } from "./key-exchange.js";
|
|
import { hexToBin } from "@bitauth/libauth";
|
|
import { deriveNostrPublicKeyBytes } from "./utilnostr.js";
|
|
|
|
export interface WalletRelayOptions {
|
|
uri: string;
|
|
walletPrivateKey: Uint8Array;
|
|
explicitRelayUrls?: string[];
|
|
reconnectInterval?: number;
|
|
maxReconnectAttempts?: number;
|
|
}
|
|
|
|
export interface WalletRelayResult {
|
|
client: RelayClient;
|
|
dappPublicKey: Uint8Array;
|
|
walletPublicKey: Uint8Array;
|
|
secret: string;
|
|
cleanup: () => void;
|
|
}
|
|
|
|
export function initiateWalletRelay(
|
|
statusCallback: RelayStatusCallback,
|
|
options: WalletRelayOptions,
|
|
): WalletRelayResult {
|
|
let decoded;
|
|
try {
|
|
decoded = decodeKeyExchangeURI(options.uri);
|
|
} catch (err: any) {
|
|
throw new Error(`Failed to decode connection URI: ${err.message}`, {
|
|
cause: err,
|
|
});
|
|
}
|
|
|
|
const dappPublicKeyHex = decoded.publicKey;
|
|
const secret = decoded.secret;
|
|
const dappPublicKeyNostr = hexToBin(dappPublicKeyHex);
|
|
|
|
const hostname = decoded.hostname;
|
|
const protocol = decoded.protocol;
|
|
const port = decoded.port;
|
|
const relayUrl = `${protocol}://${hostname}${port === (protocol === "wss" ? DEFAULT_RELAY_PORT : 80) ? "" : `:${port}`}`;
|
|
|
|
const walletPublicKeyNostr = deriveNostrPublicKeyBytes(
|
|
options.walletPrivateKey,
|
|
);
|
|
|
|
let relayClient: RelayClient | null = null;
|
|
|
|
const wrappedCallback: RelayStatusCallback = (
|
|
payload: RelayUpdatePayload,
|
|
) => {
|
|
if (!relayClient) {
|
|
relayClient = payload.client;
|
|
}
|
|
|
|
if (payload.status.status === "connected") {
|
|
// Set dapp's pubkey so outbound messages are addressed correctly.
|
|
// The wallet's own pubkey + secret are delivered via wallet_ready.
|
|
relayClient!.setPairedPublicKey(dappPublicKeyNostr);
|
|
}
|
|
|
|
statusCallback(payload);
|
|
};
|
|
|
|
const relayUrls: string[] = [relayUrl];
|
|
if (options.explicitRelayUrls && options.explicitRelayUrls.length > 0) {
|
|
for (const explicitUrl of options.explicitRelayUrls) {
|
|
if (!relayUrls.includes(explicitUrl)) {
|
|
relayUrls.push(explicitUrl);
|
|
}
|
|
}
|
|
}
|
|
|
|
const cleanup = initiateRelay(
|
|
wrappedCallback,
|
|
options.walletPrivateKey,
|
|
dappPublicKeyNostr,
|
|
{
|
|
explicitRelayUrls: relayUrls,
|
|
reconnectInterval: options.reconnectInterval,
|
|
maxReconnectAttempts: options.maxReconnectAttempts,
|
|
},
|
|
);
|
|
|
|
const result: WalletRelayResult = {
|
|
get client() {
|
|
if (!relayClient) {
|
|
throw new Error(
|
|
"Relay client not yet initialized. Wait for connection status callback.",
|
|
);
|
|
}
|
|
return relayClient;
|
|
},
|
|
dappPublicKey: dappPublicKeyNostr,
|
|
walletPublicKey: walletPublicKeyNostr,
|
|
secret,
|
|
cleanup,
|
|
};
|
|
|
|
return result;
|
|
}
|