aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-02-24 13:01:02 -0800
committerAdam Soltys <[email protected]>2026-02-24 13:01:02 -0800
commitdb59579a2efa8416f78c855faa7d7e62048447e5 (patch)
tree28444081de0120f21f7596198937bdd52fe43495 /src/lib
parent7eec7ac0b322b510b4e2cedef62422b6043d6405 (diff)
downloadcoinos-ui-db59579a2efa8416f78c855faa7d7e62048447e5.tar.gz
coinos-ui-db59579a2efa8416f78c855faa7d7e62048447e5.zip
lint
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/ark.ts22
-rw-r--r--src/lib/crypto.ts15
-rw-r--r--src/lib/nip07.ts164
-rw-r--r--src/lib/nip17.ts45
-rw-r--r--src/lib/nip44.ts4
-rw-r--r--src/lib/walletEntropy.ts3
6 files changed, 135 insertions, 118 deletions
diff --git a/src/lib/ark.ts b/src/lib/ark.ts
index a3939a40..82d1ccce 100644
--- a/src/lib/ark.ts
+++ b/src/lib/ark.ts
@@ -104,7 +104,11 @@ export const syncTransactions = async (aid: string) => {
if (arkSending) return;
const p = _syncTransactions(aid);
syncPromise = p;
- try { return await p; } finally { if (syncPromise === p) syncPromise = null; }
+ try {
+ return await p;
+ } finally {
+ if (syncPromise === p) syncPromise = null;
+ }
};
export const vtxoKey = (v: { txid: string; vout: number }) => `${v.txid}:${v.vout}`;
@@ -131,9 +135,7 @@ let syncPromise: Promise<any> | null = null;
const withTimeout = <T>(promise: Promise<T>, ms: number, label: string) =>
Promise.race([
promise,
- new Promise<T>((_, reject) =>
- setTimeout(() => reject(new Error(`${label} timed out`)), ms),
- ),
+ new Promise<T>((_, reject) => setTimeout(() => reject(new Error(`${label} timed out`)), ms)),
]);
export const sendArk = async (address: string, amount: number) => {
@@ -158,21 +160,13 @@ export const sendArk = async (address: string, amount: number) => {
}
let txid;
try {
- txid = await withTimeout(
- wallet.sendBitcoin({ address, amount }),
- 45_000,
- "Ark send",
- );
+ txid = await withTimeout(wallet.sendBitcoin({ address, amount }), 45_000, "Ark send");
} catch (e: any) {
if (e?.message?.includes("VTXO_ALREADY_REGISTERED")) {
await wallet.settle();
const vtxos = await wallet.getVtxos({ spendableOnly: false });
if (vtxos?.length) addProcessedVtxos(vtxos.map(vtxoKey));
- txid = await withTimeout(
- wallet.sendBitcoin({ address, amount }),
- 45_000,
- "Ark send retry",
- );
+ txid = await withTimeout(wallet.sendBitcoin({ address, amount }), 45_000, "Ark send retry");
} else {
throw e;
}
diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts
index d941d894..80a6364a 100644
--- a/src/lib/crypto.ts
+++ b/src/lib/crypto.ts
@@ -2,17 +2,11 @@ export function isPrfEncrypted(value: string): boolean {
return value.startsWith("{");
}
-async function importPrfKey(
- prfKey: ArrayBuffer,
- usages: KeyUsage[],
-): Promise<CryptoKey> {
+async function importPrfKey(prfKey: ArrayBuffer, usages: KeyUsage[]): Promise<CryptoKey> {
return crypto.subtle.importKey("raw", prfKey, "AES-GCM", false, usages);
}
-export async function prfEncrypt(
- prfKey: ArrayBuffer,
- plaintext: Uint8Array,
-): Promise<string> {
+export async function prfEncrypt(prfKey: ArrayBuffer, plaintext: Uint8Array): Promise<string> {
const key = await importPrfKey(prfKey, ["encrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = new Uint8Array(
@@ -29,10 +23,7 @@ export async function prfEncrypt(
});
}
-export async function prfDecrypt(
- prfKey: ArrayBuffer,
- encrypted: string,
-): Promise<Uint8Array> {
+export async function prfDecrypt(prfKey: ArrayBuffer, encrypted: string): Promise<Uint8Array> {
const { iv, ct } = JSON.parse(encrypted);
const key = await importPrfKey(prfKey, ["decrypt"]);
const ivBytes = fromBase64Url(iv);
diff --git a/src/lib/nip07.ts b/src/lib/nip07.ts
index a0af0861..093e2ead 100644
--- a/src/lib/nip07.ts
+++ b/src/lib/nip07.ts
@@ -1,102 +1,108 @@
-import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
-import { finalizeEvent } from 'nostr-tools/pure';
-import * as libnip44 from '$lib/nip44';
-import { u } from '$lib/nip44';
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
+import { finalizeEvent } from "nostr-tools/pure";
+import * as libnip44 from "$lib/nip44";
+import { u } from "$lib/nip44";
-import { eventToSign, signer } from '$lib/store';
-import { get } from 'svelte/store';
+import { eventToSign, signer } from "$lib/store";
+import { get } from "svelte/store";
// Ensures we have access to a secret key, triggering the signer modal if needed.
// Returns the sk as Uint8Array, or null if using NIP-07 extension.
export const ensureSigner = async (userPubkey: string): Promise<Uint8Array | null> => {
- // Check if NIP-07 extension is available and matches user
- if (window.nostr) {
- try {
- const extPubkey = await window.nostr.getPublicKey();
- if (extPubkey === userPubkey) {
- return null; // Use NIP-07
- }
- } catch (e) {
- // Extension didn't work, fall through to signer
- }
+ // Check if NIP-07 extension is available and matches user
+ if (window.nostr) {
+ try {
+ const extPubkey = await window.nostr.getPublicKey();
+ if (extPubkey === userPubkey) {
+ return null; // Use NIP-07
+ }
+ } catch (e) {
+ // Extension didn't work, fall through to signer
}
+ }
- // Check if signer already has a valid sk (64 hex chars = 32 bytes)
- const currentSigner = get(signer);
- if (currentSigner?.ready && currentSigner?.params?.sk && currentSigner.params.sk.length === 64) {
- return hexToBytes(currentSigner.params.sk);
- }
+ // Check if signer already has a valid sk (64 hex chars = 32 bytes)
+ const currentSigner = get(signer);
+ if (currentSigner?.ready && currentSigner?.params?.sk && currentSigner.params.sk.length === 64) {
+ return hexToBytes(currentSigner.params.sk);
+ }
- // Clear invalid signer state before triggering modal
- if (currentSigner?.ready && (!currentSigner?.params?.sk || currentSigner.params.sk.length !== 64)) {
- signer.set(undefined);
- }
+ // Clear invalid signer state before triggering modal
+ if (
+ currentSigner?.ready &&
+ (!currentSigner?.params?.sk || currentSigner.params.sk.length !== 64)
+ ) {
+ signer.set(undefined);
+ }
- // Trigger the signer modal with a dummy event
- const dummyEvent = { kind: 14, content: 'DM encryption', tags: [], created_at: Math.floor(Date.now() / 1000) };
- eventToSign.set(dummyEvent);
+ // Trigger the signer modal with a dummy event
+ const dummyEvent = {
+ kind: 14,
+ content: "DM encryption",
+ tags: [],
+ created_at: Math.floor(Date.now() / 1000),
+ };
+ eventToSign.set(dummyEvent);
- // Wait for signer to be ready
- const signerValue = await new Promise<any>((resolve, reject) => {
- let unsubscribe: () => void;
- unsubscribe = signer.subscribe((v) => {
- if (v === 'cancel') {
- setTimeout(() => unsubscribe?.());
- eventToSign.set(null);
- reject(new Error('cancelled'));
- }
- if (v?.ready) {
- setTimeout(() => unsubscribe?.());
- eventToSign.set(null);
- resolve(v);
- }
- });
+ // Wait for signer to be ready
+ const signerValue = await new Promise<any>((resolve, reject) => {
+ let unsubscribe: () => void;
+ unsubscribe = signer.subscribe((v) => {
+ if (v === "cancel") {
+ setTimeout(() => unsubscribe?.());
+ eventToSign.set(null);
+ reject(new Error("cancelled"));
+ }
+ if (v?.ready) {
+ setTimeout(() => unsubscribe?.());
+ eventToSign.set(null);
+ resolve(v);
+ }
});
+ });
- if (signerValue?.params?.sk) {
- return hexToBytes(signerValue.params.sk);
- }
+ if (signerValue?.params?.sk) {
+ return hexToBytes(signerValue.params.sk);
+ }
- // If extension method was selected
- if (signerValue?.method === 'extension') {
- return null; // Use NIP-07
- }
+ // If extension method was selected
+ if (signerValue?.method === "extension") {
+ return null; // Use NIP-07
+ }
- throw new Error('No signing method available');
-}
+ throw new Error("No signing method available");
+};
// Signs an event using NIP-07 if available, otherwise getPrivateKey()
export const signEvent = async (event: any, user: any): Promise<any> => {
- const sk = await ensureSigner(user.pubkey);
- if (sk === null) {
- return (window as any).nostr.signEvent(event);
- } else {
- return finalizeEvent(event, sk);
- }
-}
+ const sk = await ensureSigner(user.pubkey);
+ if (sk === null) {
+ return (window as any).nostr.signEvent(event);
+ } else {
+ return finalizeEvent(event, sk);
+ }
+};
// Encrypts an event in NIP-44 using NIP-07 if available, otherwise getPrivateKey()
export const encrypt = async (json: any, user: any, receiverPK: string): Promise<string> => {
- const senderSK = await ensureSigner(user.pubkey);
- if (senderSK === null) {
- return (window as any).nostr.nip44.encrypt(
- receiverPK, JSON.stringify(json));
- } else {
- const senderSKString = bytesToHex(senderSK);
- const ckey = u.getConversationKey(senderSKString, receiverPK);
- return libnip44.encrypt(JSON.stringify(json), ckey);
- }
-}
+ const senderSK = await ensureSigner(user.pubkey);
+ if (senderSK === null) {
+ return (window as any).nostr.nip44.encrypt(receiverPK, JSON.stringify(json));
+ } else {
+ const senderSKString = bytesToHex(senderSK);
+ const ckey = u.getConversationKey(senderSKString, receiverPK);
+ return libnip44.encrypt(JSON.stringify(json), ckey);
+ }
+};
// Decrypts an event in NIP-44 using NIP-07 if available, otherwise getPrivateKey()
export const decrypt = async (encrypted: string, user: any, receiverPK: string): Promise<any> => {
- const senderSK = await ensureSigner(user.pubkey);
- if (senderSK === null) {
- return JSON.parse(await (window as any).nostr.nip44.decrypt(
- receiverPK, encrypted));
- } else {
- const senderSKString = bytesToHex(senderSK);
- const ckey = u.getConversationKey(senderSKString, receiverPK);
- return JSON.parse(libnip44.decrypt(encrypted, ckey));
- }
-}
+ const senderSK = await ensureSigner(user.pubkey);
+ if (senderSK === null) {
+ return JSON.parse(await (window as any).nostr.nip44.decrypt(receiverPK, encrypted));
+ } else {
+ const senderSKString = bytesToHex(senderSK);
+ const ckey = u.getConversationKey(senderSKString, receiverPK);
+ return JSON.parse(libnip44.decrypt(encrypted, ckey));
+ }
+};
diff --git a/src/lib/nip17.ts b/src/lib/nip17.ts
index 0398c426..97191a18 100644
--- a/src/lib/nip17.ts
+++ b/src/lib/nip17.ts
@@ -5,18 +5,18 @@ import { SimplePool } from "nostr-tools/pool";
import { Relay } from "nostr-tools/relay";
import { unwrapEvent } from "nostr-tools/nip17";
-import { ensureSigner } from '$lib/nip07';
-import { relaysSupporting } from '$lib/nip11';
-import { expired, expiration } from '$lib/nip40';
-import { encrypt, u } from '$lib/nip44';
+import { ensureSigner } from "$lib/nip07";
+import { relaysSupporting } from "$lib/nip11";
+import { expired, expiration } from "$lib/nip40";
+import { encrypt, u } from "$lib/nip44";
const TWO_DAYS = 2 * 24 * 60 * 60;
const randomTimestamp = () => Math.floor(Date.now() / 1000 - Math.random() * TWO_DAYS);
const pool = new SimplePool();
let relayListCache = new Map();
-import { PUBLIC_DM_RELAYS } from '$env/static/public';
-const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(',');
+import { PUBLIC_DM_RELAYS } from "$env/static/public";
+const DM_RELAYS_LIST = PUBLIC_DM_RELAYS.split(",");
const DM_FETCH_LIMIT = 65536;
// Create a NIP-17 message using a provided sender secret key.
@@ -283,7 +283,14 @@ const publishToPreferred = async (
}
};
-const createAndPublishMessageEvent = async (sk: Uint8Array | null, message: string, userPK: string, recipientPK: string, eventFor: string, expiryDays?: number) => {
+const createAndPublishMessageEvent = async (
+ sk: Uint8Array | null,
+ message: string,
+ userPK: string,
+ recipientPK: string,
+ eventFor: string,
+ expiryDays?: number,
+) => {
let event;
if (sk === null) {
event = await createNIP17MessageNIP07(message, userPK, recipientPK, eventFor, expiryDays);
@@ -303,12 +310,30 @@ export const send = async (message: string, user: any, recipient: any, expiryDay
if (user.pubkey === recipient.pubkey) {
return createAndPublishMessageEvent(
- sk, message, user.pubkey, recipient.pubkey, user.pubkey, expiryDays);
+ sk,
+ message,
+ user.pubkey,
+ recipient.pubkey,
+ user.pubkey,
+ expiryDays,
+ );
} else {
const p1 = createAndPublishMessageEvent(
- sk, message, user.pubkey, recipient.pubkey, recipient.pubkey, expiryDays);
+ sk,
+ message,
+ user.pubkey,
+ recipient.pubkey,
+ recipient.pubkey,
+ expiryDays,
+ );
const p2 = createAndPublishMessageEvent(
- sk, message, user.pubkey, recipient.pubkey, user.pubkey, expiryDays);
+ sk,
+ message,
+ user.pubkey,
+ recipient.pubkey,
+ user.pubkey,
+ expiryDays,
+ );
return Promise.all([p1, p2]);
}
};
diff --git a/src/lib/nip44.ts b/src/lib/nip44.ts
index 6025a54f..19384b9b 100644
--- a/src/lib/nip44.ts
+++ b/src/lib/nip44.ts
@@ -24,7 +24,9 @@ export const u = {
},
getConversationKey(privkeyA: string, pubkeyB: string): Uint8Array {
- const sharedX = secp256k1.getSharedSecret(hexToBytes(privkeyA), hexToBytes("02" + pubkeyB)).subarray(1, 33);
+ const sharedX = secp256k1
+ .getSharedSecret(hexToBytes(privkeyA), hexToBytes("02" + pubkeyB))
+ .subarray(1, 33);
return hkdf_extract(sha256, sharedX, utf8ToBytes("nip44-v2"));
},
diff --git a/src/lib/walletEntropy.ts b/src/lib/walletEntropy.ts
index f022a2cc..25bd49b8 100644
--- a/src/lib/walletEntropy.ts
+++ b/src/lib/walletEntropy.ts
@@ -10,8 +10,7 @@ const DERIVATION_EVENT = {
async function hashSignature(sig: string): Promise<ArrayBuffer> {
const sigBytes = new Uint8Array(sig.length / 2);
- for (let i = 0; i < sig.length; i += 2)
- sigBytes[i / 2] = parseInt(sig.slice(i, i + 2), 16);
+ for (let i = 0; i < sig.length; i += 2) sigBytes[i / 2] = parseInt(sig.slice(i, i + 2), 16);
return crypto.subtle.digest("SHA-256", sigBytes);
}