Initial Commit
This commit is contained in:
commit
2d858727b0
10 changed files with 1128 additions and 0 deletions
124
npubCash.ts
Normal file
124
npubCash.ts
Normal file
|
@ -0,0 +1,124 @@
|
|||
import {
|
||||
CashuMint,
|
||||
CashuWallet,
|
||||
MintQuoteState,
|
||||
type Proof,
|
||||
} from "@cashu/cashu-ts";
|
||||
|
||||
import { privateKeyFromSeedWords as nostrPrivateKeyFromSeedWords } from "nostr-tools/nip06";
|
||||
import { finalizeEvent, nip98 } from "nostr-tools";
|
||||
import type { BodyInit } from "bun";
|
||||
|
||||
interface NpubCashQuote {
|
||||
createdAt: number;
|
||||
paidAt?: number;
|
||||
expiresAt: number;
|
||||
mintUrl: string;
|
||||
quoteId: string;
|
||||
request: string;
|
||||
amount: number;
|
||||
state: "PAID" | "ISSUED" | "INFLIGHT";
|
||||
locked: boolean;
|
||||
}
|
||||
|
||||
interface NpubCashPaginationMetadata {
|
||||
total: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export default class NpubCash {
|
||||
constructor(private mnemonic: string) {}
|
||||
|
||||
private async fetchWithNip98<T>(
|
||||
{ url, method, body }: {
|
||||
url: string;
|
||||
method: string;
|
||||
body?: BodyInit | null;
|
||||
},
|
||||
): Promise<T> {
|
||||
const nostrPrivateKey = nostrPrivateKeyFromSeedWords(this.mnemonic);
|
||||
const urlWithoutQueryParams = url.split("?")[0] ?? "";
|
||||
const npubCashNip98 = await nip98.getToken(
|
||||
urlWithoutQueryParams,
|
||||
method,
|
||||
(event) => finalizeEvent(event, nostrPrivateKey),
|
||||
);
|
||||
return fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
"Authorization": `Nostr ${npubCashNip98}`,
|
||||
},
|
||||
body,
|
||||
}).then((data) => data.json() as Promise<T>);
|
||||
}
|
||||
|
||||
private async getQuotes(since: number, limit: number, offset: number) {
|
||||
return this.fetchWithNip98<
|
||||
{
|
||||
error: false;
|
||||
data: { quotes: NpubCashQuote[] };
|
||||
metadata: NpubCashPaginationMetadata;
|
||||
} | {
|
||||
error: true;
|
||||
message: string;
|
||||
}
|
||||
>({
|
||||
url:
|
||||
`https://npubx.cash/api/v2/wallet/quotes?since=${since}&limit=${limit}&offset=${offset}`,
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
private async getAllPaidQuotes(since: number) {
|
||||
const quotes: NpubCashQuote[] = [];
|
||||
let offset = 0;
|
||||
while (true) {
|
||||
const currentQuotes = await this.getQuotes(since, 50, offset);
|
||||
if (currentQuotes.error === false) {
|
||||
quotes.push(...currentQuotes.data.quotes);
|
||||
if (quotes.length >= currentQuotes.metadata.total) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
throw new Error(currentQuotes.message ?? "Unknown error");
|
||||
}
|
||||
offset += 50;
|
||||
}
|
||||
return quotes.sort((a, b) => a.createdAt - b.createdAt).filter(
|
||||
(quote) => quote.state === "PAID",
|
||||
);
|
||||
}
|
||||
|
||||
async *tryRedeemUnredeemedCashuQuotes(
|
||||
latestRedeemedCashuQuoteTimestamp: number,
|
||||
) {
|
||||
const proofs: Proof[] = [];
|
||||
const quotes: NpubCashQuote[] = await this.getAllPaidQuotes(
|
||||
latestRedeemedCashuQuoteTimestamp,
|
||||
);
|
||||
for (const quote of quotes) {
|
||||
const mint = new CashuMint(quote.mintUrl);
|
||||
const wallet = new CashuWallet(mint);
|
||||
const req = await mint.checkMintQuote(quote.quoteId);
|
||||
if (
|
||||
req.state === MintQuoteState.PAID && typeof quote.paidAt === "number"
|
||||
) {
|
||||
const newProofs = await wallet.mintProofs(
|
||||
quote.amount,
|
||||
quote.quoteId,
|
||||
);
|
||||
proofs.push(...newProofs);
|
||||
const amountReceived = newProofs.reduce(
|
||||
(sum, p) => sum + p.amount,
|
||||
0,
|
||||
);
|
||||
yield {
|
||||
quoteId: quote.quoteId,
|
||||
amountSat: amountReceived,
|
||||
timestamp: quote.paidAt,
|
||||
};
|
||||
}
|
||||
}
|
||||
return proofs;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue