diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml
new file mode 100644
index 00000000..dff334ba
--- /dev/null
+++ b/.github/workflows/format.yaml
@@ -0,0 +1,22 @@
+name: Check format
+
+on: [push, pull_request]
+
+jobs:
+ format-check:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Run format check
+ run: npm run format
diff --git a/README.md b/README.md
index f40c8676..00570169 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,11 @@
Cashu Wallet
## One-liner build & run
+
```
docker-compose up -d
```
+
access at http://localhost:3000 or serve it behind a reverse proxy.
## Install the dependencies
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 10990a9e..a92cc673 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,4 +1,4 @@
-version: '2'
+version: "2"
services:
cashu.me:
image: cashu.me
@@ -6,4 +6,4 @@ services:
container_name: cashu.me
restart: always
ports:
- - "127.0.0.1:3000:80"
+ - "127.0.0.1:3000:80"
diff --git a/extension/embedder.html b/extension/embedder.html
index 54dcd14b..fc32d1e1 100644
--- a/extension/embedder.html
+++ b/extension/embedder.html
@@ -1,4 +1,4 @@
-
+
diff --git a/index.html b/index.html
index 978574a0..964a36c4 100644
--- a/index.html
+++ b/index.html
@@ -1,4 +1,4 @@
-
+
<%= productName %>
diff --git a/package.json b/package.json
index 0882e320..c4470072 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
"build:pwa": "quasar build -m pwa",
"quasar": "quasar",
"lint": "eslint --ext .js,.vue ./",
- "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
+ "format": "prettier --write .",
"test": "vitest",
"test:ci": "vitest run"
},
diff --git a/src-capacitor/android/app/debug/output.json b/src-capacitor/android/app/debug/output.json
index dc2b1619..f8cd2add 100644
--- a/src-capacitor/android/app/debug/output.json
+++ b/src-capacitor/android/app/debug/output.json
@@ -1 +1,18 @@
-[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-debug.apk","fullName":"debug","baseName":"debug","dirName":""},"path":"app-debug.apk","properties":{}}]
\ No newline at end of file
+[
+ {
+ "outputType": { "type": "APK" },
+ "apkData": {
+ "type": "MAIN",
+ "splits": [],
+ "versionCode": 1,
+ "versionName": "1.0",
+ "enabled": true,
+ "outputFile": "app-debug.apk",
+ "fullName": "debug",
+ "baseName": "debug",
+ "dirName": ""
+ },
+ "path": "app-debug.apk",
+ "properties": {}
+ }
+]
diff --git a/src-pwa/custom-service-worker.js b/src-pwa/custom-service-worker.js
index 365ae9de..a8bb59c5 100644
--- a/src-pwa/custom-service-worker.js
+++ b/src-pwa/custom-service-worker.js
@@ -28,7 +28,7 @@ if (process.env.MODE !== "ssr" || process.env.PROD) {
registerRoute(
new NavigationRoute(
createHandlerBoundToURL(process.env.PWA_FALLBACK_HTML),
- { denylist: [/sw\.js$/, /workbox-(.)*\.js$/] }
- )
+ { denylist: [/sw\.js$/, /workbox-(.)*\.js$/] },
+ ),
);
}
diff --git a/src/boot/base.js b/src/boot/base.js
index bfb65c57..61a345af 100644
--- a/src/boot/base.js
+++ b/src/boot/base.js
@@ -152,7 +152,7 @@ window.windowMixin = {
type = "null",
position = "top",
caption = null,
- color = null
+ color = null,
) {
// failure
this.$q.notify({
@@ -194,7 +194,7 @@ window.windowMixin = {
if (this.$q.localStorage.getItem("cashu.theme")) {
document.body.setAttribute(
"data-theme",
- this.$q.localStorage.getItem("cashu.theme")
+ this.$q.localStorage.getItem("cashu.theme"),
);
} else {
this.changeColor("monochrome");
diff --git a/src/components/BalanceView.vue b/src/components/BalanceView.vue
index 903f189e..7cce4ce6 100644
--- a/src/components/BalanceView.vue
+++ b/src/components/BalanceView.vue
@@ -2,17 +2,21 @@
-
+
-
-
+
+
-
+
@@ -70,7 +74,7 @@
{{
formatCurrency(
(getTotalBalance / 100 / bitcoinPrice) * 100000000,
- "sat"
+ "sat",
)
}}
diff --git a/src/components/InvoiceDetailDialog.vue b/src/components/InvoiceDetailDialog.vue
index 0467236e..bb601e22 100644
--- a/src/components/InvoiceDetailDialog.vue
+++ b/src/components/InvoiceDetailDialog.vue
@@ -188,7 +188,7 @@ export default defineComponent({
let display = this.formatCurrency(
this.invoiceData.amount,
this.invoiceData.unit,
- true
+ true,
);
return display;
},
diff --git a/src/components/MintSettings.vue b/src/components/MintSettings.vue
index 9a90a22b..51a77887 100644
--- a/src/components/MintSettings.vue
+++ b/src/components/MintSettings.vue
@@ -321,7 +321,7 @@
mintSwap(
swapData.from_url.url,
swapData.to_url.url,
- swapData.amount
+ swapData.amount,
)
"
:disable="
diff --git a/src/components/PayInvoiceDialog.vue b/src/components/PayInvoiceDialog.vue
index 0d3c4337..ccb88d9d 100644
--- a/src/components/PayInvoiceDialog.vue
+++ b/src/components/PayInvoiceDialog.vue
@@ -23,7 +23,7 @@
formatCurrency(
payInvoiceData.meltQuote.response.amount,
activeUnit,
- true
+ true,
)
}}
@@ -59,8 +59,8 @@
payInvoiceData.meltQuote.error != ''
? 'Error'
: !payInvoiceData.blocking
- ? 'Pay'
- : 'Processing...'
+ ? 'Pay'
+ : 'Processing...'
"
:loading="globalMutexLock && !payInvoiceData.blocking"
class="q-px-lg"
diff --git a/src/components/QrcodeReader.vue b/src/components/QrcodeReader.vue
index 6347fbe8..6ba5fb29 100644
--- a/src/components/QrcodeReader.vue
+++ b/src/components/QrcodeReader.vue
@@ -27,7 +27,7 @@ export default {
highlightScanRegion: true,
highlightCodeOutline: true,
onDecodeError: () => {},
- }
+ },
);
this.qrScanner.start();
this.urDecoder = new URDecoder();
diff --git a/src/components/ReceiveTokenDialog.vue b/src/components/ReceiveTokenDialog.vue
index 3e773979..ce652407 100644
--- a/src/components/ReceiveTokenDialog.vue
+++ b/src/components/ReceiveTokenDialog.vue
@@ -228,7 +228,7 @@ export default defineComponent({
// get the private key for the token we want to receive if it is locked with P2PK
receiveStore.receiveData.p2pkPrivateKey =
this.getPrivateKeyForP2PKEncodedToken(
- receiveStore.receiveData.tokensBase64
+ receiveStore.receiveData.tokensBase64,
);
const tokenJson = token.decode(receiveStore.receiveData.tokensBase64);
@@ -314,7 +314,7 @@ export default defineComponent({
// get amount from decodedToken.token.proofs[..].amount
const amount = this.getProofs(decodedToken).reduce(
(sum, el) => (sum += el.amount),
- 0
+ 0,
);
tokensStore.addPendingToken({
diff --git a/src/components/SendPaymentRequest.vue b/src/components/SendPaymentRequest.vue
index c0273ba4..cdd6247d 100644
--- a/src/components/SendPaymentRequest.vue
+++ b/src/components/SendPaymentRequest.vue
@@ -48,7 +48,7 @@ export default defineComponent({
clickPaymentRequest: function () {
this.parseAndPayPaymentRequest(
this.sendData.paymentRequest,
- this.sendData.tokensBase64
+ this.sendData.tokensBase64,
);
},
getPaymentRequestTransportType: function (request) {
diff --git a/src/components/SendTokenDialog.vue b/src/components/SendTokenDialog.vue
index 426b8a9c..dad4b9eb 100644
--- a/src/components/SendTokenDialog.vue
+++ b/src/components/SendTokenDialog.vue
@@ -479,7 +479,7 @@ export default defineComponent({
let selectedProofs = this.coinSelect(
spendableProofs,
this.sendData.amount * this.activeUnitCurrencyMultiplyer,
- this.includeFeesInSendAmount
+ this.includeFeesInSendAmount,
);
const feesToAdd = this.includeFeesInSendAmount
? this.getFeesForProofs(selectedProofs)
@@ -607,7 +607,7 @@ export default defineComponent({
}
console.log(
"### this.currentFragmentInterval",
- this.currentFragmentInterval
+ this.currentFragmentInterval,
);
this.startQrCodeLoop();
},
@@ -658,7 +658,7 @@ export default defineComponent({
let { _, sendProofs } = await this.sendToLock(
this.activeProofs,
sendAmount,
- this.sendData.p2pkPubkey
+ this.sendData.p2pkPubkey,
);
// update UI
this.sendData.tokens = sendProofs;
@@ -698,7 +698,7 @@ export default defineComponent({
this.activeProofs,
sendAmount,
true,
- this.includeFeesInSendAmount
+ this.includeFeesInSendAmount,
);
// update UI
diff --git a/src/components/SettingsView.vue b/src/components/SettingsView.vue
index cac3e3ab..29b0c5b7 100644
--- a/src/components/SettingsView.vue
+++ b/src/components/SettingsView.vue
@@ -1075,7 +1075,7 @@ export default defineComponent({
for (let mint of mints) {
const mintIds = mint.keysets.map((keyset) => keyset.id);
const keysetCounterThisMint = this.keysetCounters.filter((entry) =>
- mintIds.includes(entry.id)
+ mintIds.includes(entry.id),
);
keysetCountersByMint[mint.url] = keysetCounterThisMint;
}
diff --git a/src/components/TokenInformation.vue b/src/components/TokenInformation.vue
index 6dc8ffbc..31246688 100644
--- a/src/components/TokenInformation.vue
+++ b/src/components/TokenInformation.vue
@@ -100,7 +100,7 @@ export default defineComponent({
let uniqueIds = [...new Set(proofs.map((p) => p.id))];
// mints that have any of the keyset IDs
let mints_keysets = this.mints.filter((m) =>
- m.keysets.some((r) => uniqueIds.indexOf(r) >= 0)
+ m.keysets.some((r) => uniqueIds.indexOf(r) >= 0),
);
// what we put into the JSON
let mints = mints_keysets.map((m) => [{ url: m.url, ids: m.keysets }][0]);
@@ -116,7 +116,7 @@ export default defineComponent({
// mints that have any of the keyset IDs
return (
this.mints.filter((m) =>
- m.keysets.some((r) => uniqueIds.indexOf(r.id) >= 0)
+ m.keysets.some((r) => uniqueIds.indexOf(r.id) >= 0),
).length > 0
);
},
diff --git a/src/css/base.scss b/src/css/base.scss
index 2eca4e93..d6a5eb45 100644
--- a/src/css/base.scss
+++ b/src/css/base.scss
@@ -213,14 +213,18 @@ video {
}
.q-card {
- box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2), 0 2px 2px rgba(0, 0, 0, 0.14),
+ box-shadow:
+ 0 1px 5px rgba(0, 0, 0, 0.2),
+ 0 2px 2px rgba(0, 0, 0, 0.14),
0 3px 1px -2px rgba(0, 0, 0, 0.12);
border-radius: 4px;
vertical-align: top;
}
.shadow-2 {
- box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2), 0 2px 2px rgba(0, 0, 0, 0.14),
+ box-shadow:
+ 0 1px 5px rgba(0, 0, 0, 0.2),
+ 0 2px 2px rgba(0, 0, 0, 0.14),
0 3px 1px -2px rgba(0, 0, 0, 0.12);
}
diff --git a/src/js/notify.ts b/src/js/notify.ts
index 753fbb66..c7217111 100644
--- a/src/js/notify.ts
+++ b/src/js/notify.ts
@@ -7,7 +7,11 @@ const errorTypes = {
500: "negative",
} as StatusMap;
-async function notifyApiError(error: Error, caption: string = "", position = "top" as QNotifyCreateOptions["position"]) {
+async function notifyApiError(
+ error: Error,
+ caption: string = "",
+ position = "top" as QNotifyCreateOptions["position"],
+) {
try {
Notify.create({
timeout: 5000,
@@ -20,7 +24,7 @@ async function notifyApiError(error: Error, caption: string = "", position = "to
{
icon: "close",
color: "white",
- handler: () => { },
+ handler: () => {},
},
],
});
@@ -31,7 +35,7 @@ async function notifyApiError(error: Error, caption: string = "", position = "to
async function notifySuccess(
message: string,
- position = "top" as QNotifyCreateOptions["position"]
+ position = "top" as QNotifyCreateOptions["position"],
) {
Notify.create({
timeout: 5000,
@@ -43,7 +47,7 @@ async function notifySuccess(
{
icon: "close",
color: "white",
- handler: () => { },
+ handler: () => {},
},
],
});
@@ -60,7 +64,7 @@ async function notifyError(message: string, caption?: string) {
{
icon: "close",
color: "white",
- handler: () => { },
+ handler: () => {},
},
],
});
@@ -69,7 +73,7 @@ async function notifyError(message: string, caption?: string) {
async function notifyWarning(
message: string,
caption?: string,
- timeout = 5000
+ timeout = 5000,
) {
Notify.create({
timeout: timeout,
@@ -82,13 +86,16 @@ async function notifyWarning(
{
icon: "close",
color: "black",
- handler: () => { },
+ handler: () => {},
},
],
});
}
-async function notify(message: string, position = "top" as QNotifyCreateOptions["position"]) {
+async function notify(
+ message: string,
+ position = "top" as QNotifyCreateOptions["position"],
+) {
// failure
Notify.create({
timeout: 5000,
@@ -100,7 +107,7 @@ async function notify(message: string, position = "top" as QNotifyCreateOptions[
{
icon: "close",
color: "white",
- handler: () => { },
+ handler: () => {},
},
],
});
diff --git a/src/js/token.ts b/src/js/token.ts
index 5f0bc090..e58fc45b 100644
--- a/src/js/token.ts
+++ b/src/js/token.ts
@@ -14,9 +14,7 @@ function decode(encoded_token: string) {
* Returns a list of proofs from a decoded token
*/
function getProofs(decoded_token: Token): WalletProof[] {
- if (
- !(decoded_token.proofs.length > 0)
- ) {
+ if (!(decoded_token.proofs.length > 0)) {
throw new Error("Token format wrong");
}
const proofs = decoded_token.proofs.flat();
diff --git a/src/pages/WalletPage.vue b/src/pages/WalletPage.vue
index b60702a6..9cd8a49d 100644
--- a/src/pages/WalletPage.vue
+++ b/src/pages/WalletPage.vue
@@ -498,13 +498,13 @@ export default {
this.deferredPWAInstallPrompt = e;
console.log(
`'beforeinstallprompt' event was fired.`,
- this.getPwaDisplayMode()
+ this.getPwaDisplayMode(),
);
});
},
getPwaDisplayMode: function () {
const isStandalone = window.matchMedia(
- "(display-mode: standalone)"
+ "(display-mode: standalone)",
).matches;
if (document.referrer.startsWith("android-app://")) {
return "twa";
@@ -533,7 +533,7 @@ export default {
sessionStorage.setItem(
"tabId",
Math.random().toString(36).substring(2) +
- new Date().getTime().toString(36)
+ new Date().getTime().toString(36),
);
}
const tabId = sessionStorage.getItem("tabId");
@@ -625,7 +625,7 @@ export default {
window.history.pushState(
{},
document.title,
- window.location.href.split("?")[0].split("#")[0]
+ window.location.href.split("?")[0].split("#")[0],
);
// startup tasks
diff --git a/src/router/index.js b/src/router/index.js
index 544b4fee..e7424f11 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -20,8 +20,8 @@ export default route(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: process.env.VUE_ROUTER_MODE === "history"
- ? createWebHistory
- : createWebHashHistory;
+ ? createWebHistory
+ : createWebHashHistory;
const Router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
diff --git a/src/stores/mints.ts b/src/stores/mints.ts
index 80316f97..f97b2359 100644
--- a/src/stores/mints.ts
+++ b/src/stores/mints.ts
@@ -2,7 +2,16 @@ import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core";
import { useWorkersStore } from "./workers";
import { notifyApiError, notifyError, notifySuccess } from "src/js/notify";
-import { CashuMint, MintKeys, MintAllKeysets, MintActiveKeys, Proof, SerializedBlindedSignature, MintKeyset, GetInfoResponse } from "@cashu/cashu-ts";
+import {
+ CashuMint,
+ MintKeys,
+ MintAllKeysets,
+ MintActiveKeys,
+ Proof,
+ SerializedBlindedSignature,
+ MintKeyset,
+ GetInfoResponse,
+} from "@cashu/cashu-ts";
import { useUiStore } from "./ui";
export type Mint = {
url: string;
@@ -23,7 +32,9 @@ export class MintClass {
}
get proofs() {
const mintStore = useMintsStore();
- return mintStore.proofs.filter((p) => this.mint.keysets.map((k) => k.id).includes(p.id));
+ return mintStore.proofs.filter((p) =>
+ this.mint.keysets.map((k) => k.id).includes(p.id),
+ );
}
// get balance() {
// const proofs = this.proofs;
@@ -44,7 +55,9 @@ export class MintClass {
}
get units() {
- return this.mint.keysets.map((k) => k.unit).filter((value, index, self) => self.indexOf(value) === index);
+ return this.mint.keysets
+ .map((k) => k.unit)
+ .filter((value, index, self) => self.indexOf(value) === index);
}
unitKeysets(unit: string): MintKeyset[] {
@@ -53,7 +66,9 @@ export class MintClass {
unitProofs(unit: string) {
const unitKeysets = this.unitKeysets(unit);
- return this.proofs.filter((p) => unitKeysets.map((k) => k.id).includes(p.id));
+ return this.proofs.filter((p) =>
+ unitKeysets.map((k) => k.id).includes(p.id),
+ );
}
unitBalance(unit: string) {
@@ -69,7 +84,6 @@ export type Balances = {
[unit: string]: number;
};
-
type BlindSignatureAudit = {
signature: SerializedBlindedSignature;
amount: number;
@@ -90,7 +104,10 @@ export const useMintsStore = defineStore("mints", {
mints: useLocalStorage("cashu.mints", [] as Mint[]),
proofs: useLocalStorage("cashu.proofs", [] as WalletProof[]),
spentProofs: useLocalStorage("cashu.spentProofs", [] as WalletProof[]),
- blindSignatures: useLocalStorage("cashu.blindSignatures", [] as BlindSignatureAudit[]),
+ blindSignatures: useLocalStorage(
+ "cashu.blindSignatures",
+ [] as BlindSignatureAudit[],
+ ),
// balances: useLocalStorage("cashu.balances", {} as Balances),
showAddMintDialog: false,
addMintBlocking: false,
@@ -101,30 +118,39 @@ export const useMintsStore = defineStore("mints", {
},
getters: {
activeProofs({ activeMintUrl, activeUnit }): WalletProof[] {
- const unitKeysets = this.mints.find((m) => m.url === activeMintUrl)?.keysets?.filter((k) => k.unit === activeUnit);
+ const unitKeysets = this.mints
+ .find((m) => m.url === activeMintUrl)
+ ?.keysets?.filter((k) => k.unit === activeUnit);
if (!unitKeysets) {
return [];
}
return this.proofs.filter((p) =>
- unitKeysets.map((k) => k.id).includes(p.id)
+ unitKeysets.map((k) => k.id).includes(p.id),
);
},
activeBalance({ activeUnit }): number {
- const allUnitKeysets = this.mints.map((m) => m.keysets).flat().filter((k) => k.unit === activeUnit);
- const balance = this.proofs.filter((p) =>
- allUnitKeysets.map((k) => k.id).includes(p.id)
- ).reduce((sum, p) => sum + p.amount, 0);
- return balance
+ const allUnitKeysets = this.mints
+ .map((m) => m.keysets)
+ .flat()
+ .filter((k) => k.unit === activeUnit);
+ const balance = this.proofs
+ .filter((p) => allUnitKeysets.map((k) => k.id).includes(p.id))
+ .reduce((sum, p) => sum + p.amount, 0);
+ return balance;
},
activeKeysets({ activeMintUrl, activeUnit }): MintKeyset[] {
- const unitKeysets = this.mints.find((m) => m.url === activeMintUrl)?.keysets?.filter((k) => k.unit === activeUnit);
+ const unitKeysets = this.mints
+ .find((m) => m.url === activeMintUrl)
+ ?.keysets?.filter((k) => k.unit === activeUnit);
if (!unitKeysets) {
return [];
}
return unitKeysets;
},
activeKeys({ activeMintUrl, activeUnit }): MintKeys[] {
- const unitKeys = this.mints.find((m) => m.url === activeMintUrl)?.keys?.filter((k) => k.unit === activeUnit);
+ const unitKeys = this.mints
+ .find((m) => m.url === activeMintUrl)
+ ?.keys?.filter((k) => k.unit === activeUnit);
if (!unitKeys) {
return [];
}
@@ -163,8 +189,10 @@ export const useMintsStore = defineStore("mints", {
return new MintClass(mint);
} else {
if (this.mints.length) {
- console.error("No active mint. This should not happen. switching to first one.")
- this.activateMintUrl(this.mints[0].url, false, true)
+ console.error(
+ "No active mint. This should not happen. switching to first one.",
+ );
+ this.activateMintUrl(this.mints[0].url, false, true);
return new MintClass(this.mints[0]);
}
throw new Error("No active mint");
@@ -202,7 +230,12 @@ export const useMintsStore = defineStore("mints", {
});
this.spentProofs = this.spentProofs.concat(walletProofs);
},
- appendBlindSignatures(signature: SerializedBlindedSignature, amount: number, secret: Uint8Array, r: Uint8Array) {
+ appendBlindSignatures(
+ signature: SerializedBlindedSignature,
+ amount: number,
+ secret: Uint8Array,
+ r: Uint8Array,
+ ) {
const audit: BlindSignatureAudit = {
signature: signature,
amount: amount,
@@ -215,8 +248,11 @@ export const useMintsStore = defineStore("mints", {
toggleActiveUnitForMint(mint: Mint) {
// method to set the active unit to one that is supported by `mint`
const mintClass = new MintClass(mint);
- if (!this.activeUnit || mintClass.allBalances[this.activeUnit] == undefined) {
- this.activeUnit = mintClass.units[0]
+ if (
+ !this.activeUnit ||
+ mintClass.allBalances[this.activeUnit] == undefined
+ ) {
+ this.activeUnit = mintClass.units[0];
}
},
updateMint(oldMint: Mint, newMint: Mint) {
@@ -236,21 +272,30 @@ export const useMintsStore = defineStore("mints", {
throw new Error("Mint not found");
}
},
- addMint: async function (addMintData: { url: string, nickname: string }, verbose = false) {
+ addMint: async function (
+ addMintData: { url: string; nickname: string },
+ verbose = false,
+ ) {
let url = addMintData.url;
this.addMintBlocking = true;
try {
// sanitize url
const sanitizeUrl = (url: string): string => {
- let cleanedUrl = url.trim().replace(/\/+$/, '')
- if (!/^[a-z]+:\/\//.test(cleanedUrl)) { // Check for any protocol followed by "://"
- cleanedUrl = 'https://' + cleanedUrl;
+ let cleanedUrl = url.trim().replace(/\/+$/, "");
+ if (!/^[a-z]+:\/\//.test(cleanedUrl)) {
+ // Check for any protocol followed by "://"
+ cleanedUrl = "https://" + cleanedUrl;
}
return cleanedUrl;
};
url = sanitizeUrl(url);
- const mintToAdd: Mint = { url: url, keys: [], keysets: [], nickname: addMintData.nickname };
+ const mintToAdd: Mint = {
+ url: url,
+ keys: [],
+ keysets: [],
+ nickname: addMintData.nickname,
+ };
// we have no mints at all
if (this.mints.length === 0) {
@@ -279,7 +324,12 @@ export const useMintsStore = defineStore("mints", {
this.addMintBlocking = false;
}
},
- activateMintUrl: async function (url: string, verbose = false, force = false, unit: string | undefined = undefined) {
+ activateMintUrl: async function (
+ url: string,
+ verbose = false,
+ force = false,
+ unit: string | undefined = undefined,
+ ) {
const mint = this.mints.filter((m) => m.url === url)[0];
if (mint) {
await this.activateMint(mint, verbose, force);
@@ -312,7 +362,6 @@ export const useMintsStore = defineStore("mints", {
worker.clearAllWorkers();
},
activateMint: async function (mint: Mint, verbose = false, force = false) {
-
if (mint.url === this.activeMintUrl && !force) {
return;
}
@@ -334,10 +383,7 @@ export const useMintsStore = defineStore("mints", {
if (verbose) {
await notifySuccess("Mint activated.");
}
- console.log(
- "### activateMint: Mint activated: ",
- this.activeMintUrl,
- );
+ console.log("### activateMint: Mint activated: ", this.activeMintUrl);
} catch (error: any) {
// restore previous values because the activation errored
this.activeMintUrl = previousUrl;
@@ -360,7 +406,7 @@ export const useMintsStore = defineStore("mints", {
console.error(error);
try {
notifyApiError(error, "Could not get mint info");
- } catch { }
+ } catch {}
throw error;
}
},
@@ -388,7 +434,9 @@ export const useMintsStore = defineStore("mints", {
if (!mint.keys.find((k) => k.id === keyset.id)) {
const keys = await mintClass.api.getKeys(keyset.id);
// store keys in mint and update local storage
- this.mints.filter((m) => m.url === mint.url)[0].keys.push(keys.keysets[0]);
+ this.mints
+ .filter((m) => m.url === mint.url)[0]
+ .keys.push(keys.keysets[0]);
}
}
@@ -398,12 +446,12 @@ export const useMintsStore = defineStore("mints", {
// this.mints.filter((m) => m.url === mint.url)[0].keys = keys.keysets;
// return the mint with keys set
- return this.mints.filter((m) => m.url === mint.url)[0]
+ return this.mints.filter((m) => m.url === mint.url)[0];
} catch (error: any) {
console.error(error);
try {
notifyApiError(error, "Could not get mint keys");
- } catch { }
+ } catch {}
throw error;
}
},
@@ -417,7 +465,7 @@ export const useMintsStore = defineStore("mints", {
console.error(error);
try {
notifyApiError(error, "Could not get mint keysets");
- } catch { }
+ } catch {}
throw error;
}
},
@@ -458,5 +506,5 @@ export const useMintsStore = defineStore("mints", {
// }
// return null
// }
- }
+ },
});
diff --git a/src/stores/nostr.ts b/src/stores/nostr.ts
index b76d1eed..a1e6efb7 100644
--- a/src/stores/nostr.ts
+++ b/src/stores/nostr.ts
@@ -1,15 +1,38 @@
import { defineStore } from "pinia";
-import NDK, { NDKEvent, NDKSigner, NDKNip07Signer, NDKNip46Signer, NDKFilter, NDKPrivateKeySigner, NostrEvent, NDKKind, NDKRelaySet, NDKRelay, NDKTag, ProfilePointer } from "@nostr-dev-kit/ndk";
-import { nip04, nip19, nip44 } from 'nostr-tools'
-import { bytesToHex, hexToBytes } from '@noble/hashes/utils' // already an installed dependency
+import NDK, {
+ NDKEvent,
+ NDKSigner,
+ NDKNip07Signer,
+ NDKNip46Signer,
+ NDKFilter,
+ NDKPrivateKeySigner,
+ NostrEvent,
+ NDKKind,
+ NDKRelaySet,
+ NDKRelay,
+ NDKTag,
+ ProfilePointer,
+} from "@nostr-dev-kit/ndk";
+import { nip04, nip19, nip44 } from "nostr-tools";
+import { bytesToHex, hexToBytes } from "@noble/hashes/utils"; // already an installed dependency
import { useWalletStore } from "./wallet";
-import { generateSecretKey, getPublicKey } from 'nostr-tools'
+import { generateSecretKey, getPublicKey } from "nostr-tools";
import { useLocalStorage } from "@vueuse/core";
import { useSettingsStore } from "./settings";
import { useReceiveTokensStore } from "./receiveTokensStore";
-import { getEncodedTokenV4, PaymentRequestPayload, Token } from "@cashu/cashu-ts";
+import {
+ getEncodedTokenV4,
+ PaymentRequestPayload,
+ Token,
+} from "@cashu/cashu-ts";
import { useTokensStore } from "./tokens";
-import { notifyApiError, notifyError, notifySuccess, notifyWarning, notify } from "../js/notify";
+import {
+ notifyApiError,
+ notifyError,
+ notifySuccess,
+ notifyWarning,
+ notify,
+} from "../js/notify";
import { useSendTokensStore } from "./sendTokensStore";
import { usePRStore } from "./payment-request";
import token from "../js/token";
@@ -21,15 +44,15 @@ type MintRecommendation = {
};
type NostrEventLog = {
- id: string,
- created_at: number,
-}
+ id: string;
+ created_at: number;
+};
export enum SignerType {
NIP07 = "NIP07",
NIP46 = "NIP46",
PRIVATEKEY = "PRIVATEKEY",
- SEED = "SEED"
+ SEED = "SEED",
}
export const useNostrStore = defineStore("nostr", {
@@ -38,19 +61,37 @@ export const useNostrStore = defineStore("nostr", {
pubkey: useLocalStorage("cashu.ndk.pubkey", ""),
relays: useSettingsStore().defaultNostrRelays,
ndk: {} as NDK,
- signerType: useLocalStorage("cashu.ndk.signerType", SignerType.SEED),
+ signerType: useLocalStorage(
+ "cashu.ndk.signerType",
+ SignerType.SEED,
+ ),
nip07signer: {} as NDKNip07Signer,
nip46Token: useLocalStorage("cashu.ndk.nip46Token", ""),
nip46signer: {} as NDKNip46Signer,
- privateKeySignerPrivateKey: useLocalStorage("cashu.ndk.privateKeySignerPrivateKey", ""),
- seedSignerPrivateKey: useLocalStorage("cashu.ndk.seedSignerPrivateKey", ""),
+ privateKeySignerPrivateKey: useLocalStorage(
+ "cashu.ndk.privateKeySignerPrivateKey",
+ "",
+ ),
+ seedSignerPrivateKey: useLocalStorage(
+ "cashu.ndk.seedSignerPrivateKey",
+ "",
+ ),
seedSignerPrivateKeyNsec: "",
privateKeySigner: {} as NDKPrivateKeySigner,
signer: {} as NDKSigner,
- mintRecommendations: useLocalStorage("cashu.ndk.mintRecommendations", []),
+ mintRecommendations: useLocalStorage(
+ "cashu.ndk.mintRecommendations",
+ [],
+ ),
initialized: false,
- lastEventTimestamp: useLocalStorage("cashu.ndk.lastEventTimestamp", 0),
- nip17EventIdsWeHaveSeen: useLocalStorage("cashu.ndk.nip17EventIdsWeHaveSeen", []),
+ lastEventTimestamp: useLocalStorage(
+ "cashu.ndk.lastEventTimestamp",
+ 0,
+ ),
+ nip17EventIdsWeHaveSeen: useLocalStorage(
+ "cashu.ndk.nip17EventIdsWeHaveSeen",
+ [],
+ ),
}),
getters: {
seedSignerPrivateKeyNsec: (state) => {
@@ -89,14 +130,14 @@ export const useNostrStore = defineStore("nostr", {
this.initialized = true;
},
setSigner: async function (signer: NDKSigner) {
- this.signer = signer
- this.ndk = new NDK({ signer: signer, explicitRelayUrls: this.relays })
+ this.signer = signer;
+ this.ndk = new NDK({ signer: signer, explicitRelayUrls: this.relays });
},
signDummyEvent: async function (): Promise {
const ndkEvent = new NDKEvent();
ndkEvent.kind = 1;
ndkEvent.content = "Hello, world!";
- const sig = await ndkEvent.sign(this.signer)
+ const sig = await ndkEvent.sign(this.signer);
console.log(`nostr signature: ${sig})`);
const eventString = JSON.stringify(ndkEvent.rawEvent());
console.log(`nostr event: ${eventString}`);
@@ -107,7 +148,7 @@ export const useNostrStore = defineStore("nostr", {
this.pubkey = pubkey;
},
checkNip07Signer: async function (): Promise {
- const signer = new NDKNip07Signer()
+ const signer = new NDKNip07Signer();
try {
await signer.user();
return true;
@@ -116,10 +157,13 @@ export const useNostrStore = defineStore("nostr", {
}
},
initNip07Signer: async function () {
- const signer = new NDKNip07Signer()
+ const signer = new NDKNip07Signer();
signer.user().then(async (user) => {
if (!!user.npub) {
- console.log("Permission granted to read their public key:", user.npub);
+ console.log(
+ "Permission granted to read their public key:",
+ user.npub,
+ );
const me = this.ndk.getUser({
npub: user.npub,
});
@@ -133,7 +177,9 @@ export const useNostrStore = defineStore("nostr", {
initNip46Signer: async function (nip46Token?: string) {
const ndk = new NDK({ explicitRelayUrls: this.relays });
if (!nip46Token && !this.nip46Token.length) {
- nip46Token = await prompt("Enter your NIP-46 connection string") as string;
+ nip46Token = (await prompt(
+ "Enter your NIP-46 connection string",
+ )) as string;
if (!nip46Token) {
return;
}
@@ -143,14 +189,16 @@ export const useNostrStore = defineStore("nostr", {
this.nip46Token = nip46Token;
}
}
- const signer = new NDKNip46Signer(ndk, this.nip46Token)
+ const signer = new NDKNip46Signer(ndk, this.nip46Token);
this.signerType = SignerType.NIP46;
await this.setSigner(signer);
// If the backend sends an auth_url event, open that URL as a popup so the user can authorize the app
- signer.on("authUrl", (url) => { window.open(url, "auth", "width=600,height=600") })
+ signer.on("authUrl", (url) => {
+ window.open(url, "auth", "width=600,height=600");
+ });
// wait until the signer is ready
- const loggedinUser = await signer.blockUntilReady()
- alert("You are now logged in as " + loggedinUser.npub)
+ const loggedinUser = await signer.blockUntilReady();
+ alert("You are now logged in as " + loggedinUser.npub);
this.setPubkey(loggedinUser.pubkey);
},
resetNip46Signer: async function () {
@@ -160,19 +208,21 @@ export const useNostrStore = defineStore("nostr", {
initPrivateKeySigner: async function (nsec?: string) {
let privateKeyBytes: Uint8Array;
if (!nsec && !this.privateKeySignerPrivateKey.length) {
- nsec = await prompt("Enter your nsec") as string;
+ nsec = (await prompt("Enter your nsec")) as string;
if (!nsec) {
return;
}
- privateKeyBytes = nip19.decode(nsec).data as Uint8Array
+ privateKeyBytes = nip19.decode(nsec).data as Uint8Array;
} else {
if (nsec) {
- privateKeyBytes = nip19.decode(nsec).data as Uint8Array
+ privateKeyBytes = nip19.decode(nsec).data as Uint8Array;
} else {
privateKeyBytes = hexToBytes(this.privateKeySignerPrivateKey);
}
}
- this.privateKeySigner = new NDKPrivateKeySigner(this.privateKeySignerPrivateKey);
+ this.privateKeySigner = new NDKPrivateKeySigner(
+ this.privateKeySignerPrivateKey,
+ );
this.privateKeySignerPrivateKey = bytesToHex(privateKeyBytes);
this.signerType = SignerType.PRIVATEKEY;
await this.setSigner(this.privateKeySigner);
@@ -185,11 +235,11 @@ export const useNostrStore = defineStore("nostr", {
},
initWalletSeedPrivateKeySigner: async function () {
const walletStore = useWalletStore();
- const sk = walletStore.seed.slice(0, 32)
- const walletPublicKeyHex = getPublicKey(sk) // `pk` is a hex string
- const walletPrivateKeyHex = bytesToHex(sk)
+ const sk = walletStore.seed.slice(0, 32);
+ const walletPublicKeyHex = getPublicKey(sk); // `pk` is a hex string
+ const walletPrivateKeyHex = bytesToHex(sk);
this.seedSignerPrivateKey = walletPrivateKeyHex;
- this.privateKeySigner = new NDKPrivateKeySigner(walletPrivateKeyHex)
+ this.privateKeySigner = new NDKPrivateKeySigner(walletPrivateKeyHex);
this.signerType = SignerType.SEED;
this.setSigner(this.privateKeySigner);
this.setPubkey(walletPublicKeyHex);
@@ -205,12 +255,15 @@ export const useNostrStore = defineStore("nostr", {
events.forEach((event) => {
if (event.tagValue("k") == "38172" && event.tagValue("u")) {
const mintUrl = event.tagValue("u");
- if (typeof mintUrl === "string" && mintUrl.length > 0 && mintUrl.startsWith("https://")) {
+ if (
+ typeof mintUrl === "string" &&
+ mintUrl.length > 0 &&
+ mintUrl.startsWith("https://")
+ ) {
mintUrls.push(mintUrl);
}
}
- }
- );
+ });
// Count the number of times each mint URL appears
const mintUrlsSet = new Set(mintUrls);
const mintUrlsArray = Array.from(mintUrlsSet);
@@ -221,18 +274,24 @@ export const useNostrStore = defineStore("nostr", {
this.mintRecommendations = mintUrlsCounted;
return mintUrlsCounted;
},
- sendNip04DirectMessage: async function (recipient: string, message: string) {
+ sendNip04DirectMessage: async function (
+ recipient: string,
+ message: string,
+ ) {
// const randomPrivateKey = generateSecretKey();
// const randomPublicKey = getPublicKey(randomPrivateKey);
const randomPrivateKey = hexToBytes(this.seedSignerPrivateKey);
const randomPublicKey = this.pubkey;
- const ndk = new NDK({ explicitRelayUrls: this.relays, signer: new NDKPrivateKeySigner(bytesToHex(randomPrivateKey)) });
+ const ndk = new NDK({
+ explicitRelayUrls: this.relays,
+ signer: new NDKPrivateKeySigner(bytesToHex(randomPrivateKey)),
+ });
const event = new NDKEvent(ndk);
ndk.connect();
event.kind = NDKKind.EncryptedDirectMessage;
event.content = await nip04.encrypt(randomPrivateKey, recipient, message);
- event.tags = [['p', recipient]];
- event.sign()
+ event.tags = [["p", recipient]];
+ event.sign();
try {
await event.publish();
notifySuccess("NIP-04 event published");
@@ -243,11 +302,13 @@ export const useNostrStore = defineStore("nostr", {
},
subscribeToNip04DirectMessages: async function () {
let nip04DirectMessageEvents: Set = new Set();
- const fetchEventsPromise = new Promise>(resolve => {
+ const fetchEventsPromise = new Promise>((resolve) => {
if (!this.lastEventTimestamp) {
this.lastEventTimestamp = Math.floor(Date.now() / 1000);
}
- console.log(`### Subscribing to NIP-04 direct messages to ${this.pubkey} since ${this.lastEventTimestamp}`);
+ console.log(
+ `### Subscribing to NIP-04 direct messages to ${this.pubkey} since ${this.lastEventTimestamp}`,
+ );
this.ndk.connect();
const sub = this.ndk.subscribe(
{
@@ -257,41 +318,58 @@ export const useNostrStore = defineStore("nostr", {
} as NDKFilter,
{ closeOnEose: false, groupable: false },
);
- sub.on('event', (event: NDKEvent) => {
- console.log('event')
- nip04.decrypt(hexToBytes(this.seedSignerPrivateKey), event.pubkey, event.content).then((content) => {
- console.log('NIP-04 DM from', event.pubkey);
- console.log("Content:", content);
- nip04DirectMessageEvents.add(event)
- this.lastEventTimestamp = Math.floor(Date.now() / 1000);
- this.parseMessageForEcash(content);
- });
+ sub.on("event", (event: NDKEvent) => {
+ console.log("event");
+ nip04
+ .decrypt(
+ hexToBytes(this.seedSignerPrivateKey),
+ event.pubkey,
+ event.content,
+ )
+ .then((content) => {
+ console.log("NIP-04 DM from", event.pubkey);
+ console.log("Content:", content);
+ nip04DirectMessageEvents.add(event);
+ this.lastEventTimestamp = Math.floor(Date.now() / 1000);
+ this.parseMessageForEcash(content);
+ });
});
});
try {
nip04DirectMessageEvents = await fetchEventsPromise;
} catch (error) {
- console.error('Error fetching contact events:', error);
+ console.error("Error fetching contact events:", error);
}
},
- sendNip17DirectMessageToNprofile: async function (nprofile: string, message: string) {
+ sendNip17DirectMessageToNprofile: async function (
+ nprofile: string,
+ message: string,
+ ) {
const result = nip19.decode(nprofile);
const pubkey: string = (result.data as ProfilePointer).pubkey;
- const relays: string[] | undefined = (result.data as ProfilePointer).relays;
- this.sendNip17DirectMessage(pubkey, message, relays)
+ const relays: string[] | undefined = (result.data as ProfilePointer)
+ .relays;
+ this.sendNip17DirectMessage(pubkey, message, relays);
},
randomTimeUpTo2DaysInThePast: function () {
return Math.floor(Date.now() / 1000) - Math.floor(Math.random() * 172800);
},
- sendNip17DirectMessage: async function (recipient: string, message: string, relays?: string[]) {
+ sendNip17DirectMessage: async function (
+ recipient: string,
+ message: string,
+ relays?: string[],
+ ) {
const randomPrivateKey = generateSecretKey();
const randomPublicKey = getPublicKey(randomPrivateKey);
- const ndk = new NDK({ explicitRelayUrls: relays ?? this.relays, signer: new NDKPrivateKeySigner(bytesToHex(randomPrivateKey)) });
+ const ndk = new NDK({
+ explicitRelayUrls: relays ?? this.relays,
+ signer: new NDKPrivateKeySigner(bytesToHex(randomPrivateKey)),
+ });
const dmEvent = new NDKEvent();
dmEvent.kind = 14;
dmEvent.content = message;
- dmEvent.tags = [['p', recipient]];
+ dmEvent.tags = [["p", recipient]];
dmEvent.created_at = Math.floor(Date.now() / 1000);
dmEvent.pubkey = this.pubkey;
dmEvent.id = dmEvent.getEventHash();
@@ -299,7 +377,10 @@ export const useNostrStore = defineStore("nostr", {
const sealEvent = new NDKEvent(this.ndk as NDK);
sealEvent.kind = 13;
- sealEvent.content = nip44.v2.encrypt(dmEventString, nip44.v2.utils.getConversationKey(this.seedSignerPrivateKey, recipient));
+ sealEvent.content = nip44.v2.encrypt(
+ dmEventString,
+ nip44.v2.utils.getConversationKey(this.seedSignerPrivateKey, recipient),
+ );
sealEvent.created_at = this.randomTimeUpTo2DaysInThePast();
sealEvent.pubkey = this.pubkey;
sealEvent.id = sealEvent.getEventHash();
@@ -308,8 +389,14 @@ export const useNostrStore = defineStore("nostr", {
const wrapEvent = new NDKEvent(ndk);
wrapEvent.kind = 1059;
- wrapEvent.tags = [['p', recipient]];
- wrapEvent.content = nip44.v2.encrypt(sealEventString, nip44.v2.utils.getConversationKey(bytesToHex(randomPrivateKey), recipient));
+ wrapEvent.tags = [["p", recipient]];
+ wrapEvent.content = nip44.v2.encrypt(
+ sealEventString,
+ nip44.v2.utils.getConversationKey(
+ bytesToHex(randomPrivateKey),
+ recipient,
+ ),
+ );
wrapEvent.created_at = this.randomTimeUpTo2DaysInThePast();
wrapEvent.pubkey = randomPublicKey;
wrapEvent.id = wrapEvent.getEventHash();
@@ -326,12 +413,14 @@ export const useNostrStore = defineStore("nostr", {
},
subscribeToNip17DirectMessages: async function () {
let nip17DirectMessageEvents: Set = new Set();
- const fetchEventsPromise = new Promise>(resolve => {
+ const fetchEventsPromise = new Promise>((resolve) => {
if (!this.lastEventTimestamp) {
this.lastEventTimestamp = Math.floor(Date.now() / 1000);
}
const since = this.lastEventTimestamp - 172800; // last 2 days
- console.log(`### Subscribing to NIP-17 direct messages to ${this.pubkey} since ${since}`);
+ console.log(
+ `### Subscribing to NIP-17 direct messages to ${this.pubkey} since ${since}`,
+ );
this.ndk.connect();
const sub = this.ndk.subscribe(
{
@@ -342,8 +431,11 @@ export const useNostrStore = defineStore("nostr", {
{ closeOnEose: false, groupable: false },
);
- sub.on('event', (wrapEvent: NDKEvent) => {
- const eventLog = { id: wrapEvent.id, created_at: wrapEvent.created_at } as NostrEventLog;
+ sub.on("event", (wrapEvent: NDKEvent) => {
+ const eventLog = {
+ id: wrapEvent.id,
+ created_at: wrapEvent.created_at,
+ } as NostrEventLog;
if (this.nip17EventIdsWeHaveSeen.find((e) => e.id === wrapEvent.id)) {
console.log(`### Already seen NIP-17 event ${wrapEvent.id}`);
return;
@@ -351,14 +443,28 @@ export const useNostrStore = defineStore("nostr", {
console.log(`### New event ${wrapEvent.id}`);
this.nip17EventIdsWeHaveSeen.push(eventLog);
// remove all events older than 4 days to keep the list small
- this.nip17EventIdsWeHaveSeen = this.nip17EventIdsWeHaveSeen.filter((e) => e.created_at > Math.floor(Date.now() / 1000) - 345600);
+ this.nip17EventIdsWeHaveSeen = this.nip17EventIdsWeHaveSeen.filter(
+ (e) => e.created_at > Math.floor(Date.now() / 1000) - 345600,
+ );
}
- const wappedContent = nip44.v2.decrypt(wrapEvent.content, nip44.v2.utils.getConversationKey(this.seedSignerPrivateKey, wrapEvent.pubkey))
+ const wappedContent = nip44.v2.decrypt(
+ wrapEvent.content,
+ nip44.v2.utils.getConversationKey(
+ this.seedSignerPrivateKey,
+ wrapEvent.pubkey,
+ ),
+ );
const sealEvent = JSON.parse(wappedContent) as NostrEvent;
- const dmEventString = nip44.v2.decrypt(sealEvent.content, nip44.v2.utils.getConversationKey(this.seedSignerPrivateKey, sealEvent.pubkey));
+ const dmEventString = nip44.v2.decrypt(
+ sealEvent.content,
+ nip44.v2.utils.getConversationKey(
+ this.seedSignerPrivateKey,
+ sealEvent.pubkey,
+ ),
+ );
const dmEvent = JSON.parse(dmEventString) as NDKEvent;
const content = dmEvent.content;
- nip17DirectMessageEvents.add(dmEvent)
+ nip17DirectMessageEvents.add(dmEvent);
this.lastEventTimestamp = Math.floor(Date.now() / 1000);
this.parseMessageForEcash(content);
});
@@ -366,7 +472,7 @@ export const useNostrStore = defineStore("nostr", {
try {
nip17DirectMessageEvents = await fetchEventsPromise;
} catch (error) {
- console.error('Error fetching contact events:', error);
+ console.error("Error fetching contact events:", error);
}
},
parseMessageForEcash: async function (message: string) {
@@ -400,11 +506,11 @@ export const useNostrStore = defineStore("nostr", {
prStore.showPRDialog = false;
receiveStore.showReceiveTokens = true;
- return
+ return;
}
} catch (e) {
// console.log("### parsing message for ecash failed");
- return
+ return;
}
console.log("### parsing message for ecash", message);
@@ -419,7 +525,9 @@ export const useNostrStore = defineStore("nostr", {
await this.addPendingTokenToHistory(tokenStr);
}
},
- tokenAlreadyInHistory: function (tokenStr: string): HistoryToken | undefined {
+ tokenAlreadyInHistory: function (
+ tokenStr: string,
+ ): HistoryToken | undefined {
const tokensStore = useTokensStore();
return tokensStore.historyTokens.find((t) => t.token === tokenStr);
},
@@ -433,13 +541,12 @@ export const useNostrStore = defineStore("nostr", {
const tokensStore = useTokensStore();
const decodedToken = token.decode(tokenStr);
if (decodedToken == undefined) {
- throw Error('could not decode token')
+ throw Error("could not decode token");
}
// get amount from decodedToken.token.proofs[..].amount
- const amount = token.getProofs(decodedToken).reduce(
- (sum, el) => (sum += el.amount),
- 0
- );
+ const amount = token
+ .getProofs(decodedToken)
+ .reduce((sum, el) => (sum += el.amount), 0);
tokensStore.addPendingToken({
amount: amount,
diff --git a/src/stores/npubcash.ts b/src/stores/npubcash.ts
index 5297b866..4653bf3a 100644
--- a/src/stores/npubcash.ts
+++ b/src/stores/npubcash.ts
@@ -1,12 +1,18 @@
import { defineStore } from "pinia";
import NDK, { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
import { useLocalStorage } from "@vueuse/core";
-import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency
-import { generateSecretKey, getPublicKey } from 'nostr-tools'
-import { nip19 } from 'nostr-tools'
+import { bytesToHex } from "@noble/hashes/utils"; // already an installed dependency
+import { generateSecretKey, getPublicKey } from "nostr-tools";
+import { nip19 } from "nostr-tools";
import { useWalletStore } from "./wallet";
import { useReceiveTokensStore } from "./receiveTokensStore";
-import { notifyApiError, notifyError, notifySuccess, notifyWarning, notify } from "../js/notify";
+import {
+ notifyApiError,
+ notifyError,
+ notifySuccess,
+ notifyWarning,
+ notify,
+} from "../js/notify";
import { Proof } from "@cashu/cashu-ts";
import token from "../js/token";
import { WalletProof, useMintsStore } from "./mints";
@@ -18,38 +24,38 @@ import { useNostrStore } from "../stores/nostr";
// }
type NPCInfo = {
- mintUrl: string,
- npub: string,
- username: string
- error?: string
-}
+ mintUrl: string;
+ npub: string;
+ username: string;
+ error?: string;
+};
type NPCBalance = {
- error: string,
- data: number
-}
+ error: string;
+ data: number;
+};
type NPCClaim = {
- error: string,
+ error: string;
data: {
- token: string,
- }
-}
+ token: string;
+ };
+};
type NPCWithdrawl = {
id: number;
claim_ids: number[];
created_at: number;
pubkey: string;
amount: number;
-}
+};
type NPCWithdrawals = {
- error: string,
+ error: string;
data: {
- count: number,
- withdrawals: Array
- }
-}
+ count: number;
+ withdrawals: Array;
+ };
+};
const NIP98Kind = 27235;
@@ -65,65 +71,73 @@ export const useNPCStore = defineStore("npc", {
// ndk: new NDK(),
// signer: {} as NDKPrivateKeySigner,
}),
- getters: {
- },
+ getters: {},
actions: {
generateNPCConnection: async function () {
- const nostrStore = useNostrStore()
+ const nostrStore = useNostrStore();
if (!nostrStore.pubkey) {
- return
+ return;
}
- const walletPublicKeyHex = nostrStore.pubkey
- console.log('Lightning address for wallet:', nip19.npubEncode(walletPublicKeyHex) + '@' + this.npcDomain)
- console.log('npub:', nip19.npubEncode(walletPublicKeyHex))
- this.baseURL = `https://${this.npcDomain}`
- const previousAddress = this.npcAddress
- this.npcAddress = nip19.npubEncode(walletPublicKeyHex) + '@' + this.npcDomain
+ const walletPublicKeyHex = nostrStore.pubkey;
+ console.log(
+ "Lightning address for wallet:",
+ nip19.npubEncode(walletPublicKeyHex) + "@" + this.npcDomain,
+ );
+ console.log("npub:", nip19.npubEncode(walletPublicKeyHex));
+ this.baseURL = `https://${this.npcDomain}`;
+ const previousAddress = this.npcAddress;
+ this.npcAddress =
+ nip19.npubEncode(walletPublicKeyHex) + "@" + this.npcDomain;
if (!this.npcEnabled) {
- return
+ return;
}
// get info
this.npcLoading = true;
try {
- const info = await this.getInfo()
+ const info = await this.getInfo();
if (info.error) {
- notifyError(info.error)
- return
+ notifyError(info.error);
+ return;
}
// log info
- console.log(info)
+ console.log(info);
if (info.username) {
- const usernameAddress = info.username + '@' + this.npcDomain
+ const usernameAddress = info.username + "@" + this.npcDomain;
if (previousAddress !== usernameAddress) {
- notifySuccess(`Logged in as ${info.username}`)
+ notifySuccess(`Logged in as ${info.username}`);
}
- this.npcAddress = usernameAddress
+ this.npcAddress = usernameAddress;
}
} catch (e: any) {
- notifyApiError(e)
+ notifyApiError(e);
} finally {
this.npcLoading = false;
}
-
-
},
- generateNip98Event: async function (url: string, method: string, body?: string): Promise {
- const nostrStore = useNostrStore()
- await nostrStore.initSignerIfNotSet()
+ generateNip98Event: async function (
+ url: string,
+ method: string,
+ body?: string,
+ ): Promise {
+ const nostrStore = useNostrStore();
+ await nostrStore.initSignerIfNotSet();
const nip98Event = new NDKEvent(new NDK());
nip98Event.kind = NIP98Kind;
- nip98Event.content = '';
- nip98Event.tags = [['u', url], ['method', method]];
+ nip98Event.content = "";
+ nip98Event.tags = [
+ ["u", url],
+ ["method", method],
+ ];
// TODO: if body is set, add 'payload' tag with sha256 hash of body
- const sig = await nip98Event.sign(nostrStore.signer)
+ const sig = await nip98Event.sign(nostrStore.signer);
const eventString = JSON.stringify(nip98Event.rawEvent());
// encode the eventString to base64
- return btoa(eventString)
+ return btoa(eventString);
},
getInfo: async function (): Promise {
const authHeader = await this.generateNip98Event(
`${this.baseURL}/api/v1/info`,
- "GET"
+ "GET",
);
try {
const response = await fetch(`${this.baseURL}/api/v1/info`, {
@@ -131,21 +145,21 @@ export const useNPCStore = defineStore("npc", {
headers: {
Authorization: `Nostr ${authHeader}`,
},
- })
- const info: NPCInfo = await response.json()
- return info
+ });
+ const info: NPCInfo = await response.json();
+ return info;
} catch (e) {
- console.error(e)
+ console.error(e);
return {
mintUrl: "",
npub: "",
- username: ""
- }
+ username: "",
+ };
}
},
claimAllTokens: async function () {
if (!this.npcEnabled) {
- return
+ return;
}
const receiveStore = useReceiveTokensStore();
const npubCashBalance = await this.getBalance();
@@ -155,13 +169,13 @@ export const useNPCStore = defineStore("npc", {
const token = await this.getClaim();
if (token) {
// add token to history first
- this.addPendingTokenToHistory(token)
+ this.addPendingTokenToHistory(token);
receiveStore.receiveData.tokensBase64 = token;
if (this.automaticClaim) {
try {
// redeem token automatically
- const walletStore = useWalletStore()
- await walletStore.redeem()
+ const walletStore = useWalletStore();
+ await walletStore.redeem();
} catch {
// if it doesn't work, show the receive window
receiveStore.showReceiveTokens = true;
@@ -175,34 +189,34 @@ export const useNPCStore = defineStore("npc", {
tokenAlreadyInHistory: function (tokenStr: string) {
const tokensStore = useTokensStore();
return (
- tokensStore.historyTokens.find((t) => t.token === tokenStr) !== undefined
+ tokensStore.historyTokens.find((t) => t.token === tokenStr) !==
+ undefined
);
},
addPendingTokenToHistory: function (tokenStr: string) {
const receiveStore = useReceiveTokensStore();
if (this.tokenAlreadyInHistory(tokenStr)) {
- notifySuccess("Ecash already in history")
+ notifySuccess("Ecash already in history");
receiveStore.showReceiveTokens = false;
return;
}
const tokensStore = useTokensStore();
const decodedToken = token.decode(tokenStr);
if (decodedToken == undefined) {
- throw Error('could not decode token')
+ throw Error("could not decode token");
}
// get amount from decodedToken.token.proofs[..].amount
- const amount = token.getProofs(decodedToken).reduce(
- (sum, el) => (sum += el.amount),
- 0
- );
+ const amount = token
+ .getProofs(decodedToken)
+ .reduce((sum, el) => (sum += el.amount), 0);
- const mintUrl = token.getMint(decodedToken)
- const unit = token.getUnit(decodedToken)
+ const mintUrl = token.getMint(decodedToken);
+ const unit = token.getUnit(decodedToken);
tokensStore.addPendingToken({
amount: amount,
serializedProofs: tokenStr,
mint: mintUrl,
- unit: unit
+ unit: unit,
});
receiveStore.showReceiveTokens = false;
},
@@ -210,7 +224,7 @@ export const useNPCStore = defineStore("npc", {
getBalance: async function (): Promise {
const authHeader = await this.generateNip98Event(
`${this.baseURL}/api/v1/balance`,
- "GET"
+ "GET",
);
try {
const response = await fetch(`${this.baseURL}/api/v1/balance`, {
@@ -218,22 +232,22 @@ export const useNPCStore = defineStore("npc", {
headers: {
Authorization: `Nostr ${authHeader}`,
},
- })
+ });
// deserialize the response to NPCBalance
- const balance: NPCBalance = await response.json()
+ const balance: NPCBalance = await response.json();
if (balance.error) {
- return 0
+ return 0;
}
- return balance.data
+ return balance.data;
} catch (e) {
- console.error(e)
- return 0
+ console.error(e);
+ return 0;
}
},
getClaim: async function (): Promise {
const authHeader = await this.generateNip98Event(
`${this.baseURL}/api/v1/claim`,
- "GET"
+ "GET",
);
try {
const response = await fetch(`${this.baseURL}/api/v1/claim`, {
@@ -241,17 +255,17 @@ export const useNPCStore = defineStore("npc", {
headers: {
Authorization: `Nostr ${authHeader}`,
},
- })
+ });
// deserialize the response to NPCClaim
- const claim: NPCClaim = await response.json()
+ const claim: NPCClaim = await response.json();
if (claim.error) {
- return ""
+ return "";
}
- return claim.data.token
+ return claim.data.token;
} catch (e) {
- console.error(e)
- return ""
+ console.error(e);
+ return "";
}
},
- }
+ },
});
diff --git a/src/stores/nwc.ts b/src/stores/nwc.ts
index 607784e2..fb64c0a0 100644
--- a/src/stores/nwc.ts
+++ b/src/stores/nwc.ts
@@ -1,65 +1,79 @@
import { defineStore } from "pinia";
-import NDK, { NDKEvent, NDKNip07Signer, NDKNip46Signer, NDKFilter, NDKPrivateKeySigner, NDKKind, NDKSubscription } from "@nostr-dev-kit/ndk";
+import NDK, {
+ NDKEvent,
+ NDKNip07Signer,
+ NDKNip46Signer,
+ NDKFilter,
+ NDKPrivateKeySigner,
+ NDKKind,
+ NDKSubscription,
+} from "@nostr-dev-kit/ndk";
import { useLocalStorage } from "@vueuse/core";
-import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency
-import { nip04, generateSecretKey, getPublicKey } from 'nostr-tools'
-import { useMintsStore } from './mints'
+import { bytesToHex } from "@noble/hashes/utils"; // already an installed dependency
+import { nip04, generateSecretKey, getPublicKey } from "nostr-tools";
+import { useMintsStore } from "./mints";
import { useWalletStore } from "./wallet";
import { useProofsStore } from "./proofs";
import { notify, notifyError, notifyWarning } from "../js/notify";
import { useSettingsStore } from "./settings";
type NWCConnection = {
- walletPublicKey: string,
- walletPrivateKey: string,
- connectionSecret: string,
- connectionPublicKey: string,
- allowanceLeft: number
-}
+ walletPublicKey: string;
+ walletPrivateKey: string;
+ connectionSecret: string;
+ connectionPublicKey: string;
+ allowanceLeft: number;
+};
type NWCCommand = {
- method: string,
- params: any
-}
+ method: string;
+ params: any;
+};
type NWCResult = {
- result_type: string,
- result: any
-}
+ result_type: string;
+ result: any;
+};
type NWCError = {
- result_type: string,
+ result_type: string;
error: {
- code: string,
- message: string
- }
-}
+ code: string;
+ message: string;
+ };
+};
const NWCKind = {
NWCInfo: 13194,
NWCRequest: 23194,
- NWCResponse: 23195
-}
+ NWCResponse: 23195,
+};
export const useNWCStore = defineStore("nwc", {
state: () => ({
nwcEnabled: useLocalStorage("cashu.nwc.enabled", false),
connections: useLocalStorage("cashu.nwc.connections", []),
- supportedMethods: ["pay_invoice", "get_balance", "get_info", "list_transactions"],
- relays: useLocalStorage("cashu.nwc.relays", useSettingsStore().defaultNostrRelays),
+ supportedMethods: [
+ "pay_invoice",
+ "get_balance",
+ "get_info",
+ "list_transactions",
+ ],
+ relays: useLocalStorage(
+ "cashu.nwc.relays",
+ useSettingsStore().defaultNostrRelays,
+ ),
blocking: false,
ndk: new NDK(),
subscriptions: [] as NDKSubscription[],
showNWCDialog: false,
showNWCData: { connection: {} as NWCConnection, connectionString: "" },
}),
- getters: {
-
- },
+ getters: {},
actions: {
// ––––---------- NWC Command Handlers ––––----------
handleGetInfo: async function (nwcCommand: NWCCommand) {
- console.log("### get_info", nwcCommand.method)
+ console.log("### get_info", nwcCommand.method);
return {
result_type: "get_info",
result: {
@@ -69,125 +83,131 @@ export const useNWCStore = defineStore("nwc", {
network: "mainnet",
block_height: 1,
block_hash: "blockchain disrespectoor",
- methods: this.supportedMethods
- }
- }
+ methods: this.supportedMethods,
+ },
+ };
},
handleGetBalance: async function (nwcCommand: NWCCommand) {
- const mintsStore = useMintsStore()
- console.log("### get_balance", nwcCommand.method)
+ const mintsStore = useMintsStore();
+ console.log("### get_balance", nwcCommand.method);
return {
result_type: "get_balance",
result: {
- balance: mintsStore.activeBalance * 1000
- }
- }
+ balance: mintsStore.activeBalance * 1000,
+ },
+ };
},
handlePayInvoice: async function (nwcCommand: NWCCommand) {
- const invoice = nwcCommand.params.invoice
- const amountMsat = nwcCommand.params.amount
- console.log("### pay_invoice", nwcCommand.method)
- console.log("### invoice", invoice)
- console.log("### amountMsat", amountMsat)
+ const invoice = nwcCommand.params.invoice;
+ const amountMsat = nwcCommand.params.amount;
+ console.log("### pay_invoice", nwcCommand.method);
+ console.log("### invoice", invoice);
+ console.log("### amountMsat", amountMsat);
// pay invoice
- const walletStore = useWalletStore()
+ const walletStore = useWalletStore();
const proofsStore = useProofsStore();
- const mintStore = useMintsStore()
+ const mintStore = useMintsStore();
try {
- await walletStore.decodeRequest(invoice)
+ await walletStore.decodeRequest(invoice);
} catch (e) {
- console.log("### error decoding invoice", e)
+ console.log("### error decoding invoice", e);
return {
result_type: nwcCommand.method,
- error: { code: "INTERNAL", message: "Invalid invoice" }
- } as NWCError
+ error: { code: "INTERNAL", message: "Invalid invoice" },
+ } as NWCError;
}
// expect that the melt quote was requested
- if (walletStore.payInvoiceData.meltQuote.response.amount == 0 || walletStore.payInvoiceData.meltQuote.error) {
- notifyWarning("NWC: Error requesting melt quote")
+ if (
+ walletStore.payInvoiceData.meltQuote.response.amount == 0 ||
+ walletStore.payInvoiceData.meltQuote.error
+ ) {
+ notifyWarning("NWC: Error requesting melt quote");
return {
result_type: nwcCommand.method,
- error: { code: "INTERNAL", message: "Error requesting melt quote" }
- } as NWCError
+ error: { code: "INTERNAL", message: "Error requesting melt quote" },
+ } as NWCError;
}
- const maximumAmount = walletStore.payInvoiceData.meltQuote.response.amount + walletStore.payInvoiceData.meltQuote.response.fee_reserve
+ const maximumAmount =
+ walletStore.payInvoiceData.meltQuote.response.amount +
+ walletStore.payInvoiceData.meltQuote.response.fee_reserve;
if (mintStore.activeUnit != "sat") {
- notifyWarning("NWC: Active unit must be sats")
+ notifyWarning("NWC: Active unit must be sats");
return {
result_type: nwcCommand.method,
- error: { code: "INTERNAL", message: "Your active must be sats" }
- } as NWCError
+ error: { code: "INTERNAL", message: "Your active must be sats" },
+ } as NWCError;
}
if (maximumAmount > this.connections[0].allowanceLeft) {
- notifyWarning("NWC: Allowance exceeded")
+ notifyWarning("NWC: Allowance exceeded");
return {
result_type: nwcCommand.method,
- error: { code: "QUOTA_EXCEEDED", message: "Your quota has exceeded" }
- } as NWCError
+ error: { code: "QUOTA_EXCEEDED", message: "Your quota has exceeded" },
+ } as NWCError;
}
try {
- const meltData = await walletStore.melt()
- const paidAmount = walletStore.payInvoiceData.meltQuote.response.amount + proofsStore.sumProofs(meltData.change);
+ const meltData = await walletStore.melt();
+ const paidAmount =
+ walletStore.payInvoiceData.meltQuote.response.amount +
+ proofsStore.sumProofs(meltData.change);
this.connections[0].allowanceLeft -= paidAmount;
return {
result_type: nwcCommand.method,
result: {
preimage: meltData.preimage,
- }
- }
+ },
+ };
} catch (e) {
return {
result_type: nwcCommand.method,
- error: { code: "INTERNAL", message: "Could not pay invoice" }
- } as NWCError
+ error: { code: "INTERNAL", message: "Could not pay invoice" },
+ } as NWCError;
}
-
},
handleListTransactions: async function (nwcCommand: NWCCommand) {
- console.log("### list_transactions", nwcCommand.method)
+ console.log("### list_transactions", nwcCommand.method);
type nwcTransaction = {
- type: string,
- invoice: string,
- description: string | null,
- preimage: string | null,
- payment_hash: string | null,
- amount: number,
- fees_paid: number | null,
- created_at: number,
- settled_at: number | null
- expires_at: number | null
- }
- const walletStore = useWalletStore()
- const from = nwcCommand.params.from || 0
- const until = nwcCommand.params.until || Math.floor(Date.now() / 1000)
- const limit = nwcCommand.params.limit || 10
- const offset = nwcCommand.params.offset || 0
- const unpaid = nwcCommand.params.unpaid || false
- const type = nwcCommand.params.type || undefined
-
+ type: string;
+ invoice: string;
+ description: string | null;
+ preimage: string | null;
+ payment_hash: string | null;
+ amount: number;
+ fees_paid: number | null;
+ created_at: number;
+ settled_at: number | null;
+ expires_at: number | null;
+ };
+ const walletStore = useWalletStore();
+ const from = nwcCommand.params.from || 0;
+ const until = nwcCommand.params.until || Math.floor(Date.now() / 1000);
+ const limit = nwcCommand.params.limit || 10;
+ const offset = nwcCommand.params.offset || 0;
+ const unpaid = nwcCommand.params.unpaid || false;
+ const type = nwcCommand.params.type || undefined;
- const invoiceHistory = walletStore.invoiceHistory
- const transactionsHistory = invoiceHistory.filter((invoice) => {
- const date = new Date(invoice.date)
- const created_at = Math.floor(date.getTime() / 1000)
- if (from && created_at < from) {
- return false
- }
- if (until && created_at > until) {
- return false
- }
- if (type && type == "incoming" && invoice.amount < 0) {
- return false
- }
- if (type && type == "outgoing" && invoice.amount > 0) {
- return false
- }
- if (unpaid && invoice.status == "paid") {
- return false
- }
- return true
- }
- ).slice(offset, offset + limit)
+ const invoiceHistory = walletStore.invoiceHistory;
+ const transactionsHistory = invoiceHistory
+ .filter((invoice) => {
+ const date = new Date(invoice.date);
+ const created_at = Math.floor(date.getTime() / 1000);
+ if (from && created_at < from) {
+ return false;
+ }
+ if (until && created_at > until) {
+ return false;
+ }
+ if (type && type == "incoming" && invoice.amount < 0) {
+ return false;
+ }
+ if (type && type == "outgoing" && invoice.amount > 0) {
+ return false;
+ }
+ if (unpaid && invoice.status == "paid") {
+ return false;
+ }
+ return true;
+ })
+ .slice(offset, offset + limit);
// now create an array "transactions" out of nwcTransaction from transactionsHistory
//
// type = "incoming" if amount > 0 else "outgoing"
@@ -196,10 +216,13 @@ export const useNWCStore = defineStore("nwc", {
// settled_at = unix timestamp of date if status == "paid" else null
const transactions = transactionsHistory.map((invoice) => {
- let type = invoice.amount > 0 ? "incoming" : "outgoing"
- let amount = Math.abs(invoice.amount) * 1000
- let created_at = Math.floor(new Date(invoice.date).getTime() / 1000)
- let settled_at = invoice.status == "paid" ? Math.floor(new Date(invoice.date).getTime() / 1000) : null
+ let type = invoice.amount > 0 ? "incoming" : "outgoing";
+ let amount = Math.abs(invoice.amount) * 1000;
+ let created_at = Math.floor(new Date(invoice.date).getTime() / 1000);
+ let settled_at =
+ invoice.status == "paid"
+ ? Math.floor(new Date(invoice.date).getTime() / 1000)
+ : null;
return {
type: type,
invoice: invoice.bolt11,
@@ -208,138 +231,164 @@ export const useNWCStore = defineStore("nwc", {
fees_paid: 0,
created_at: created_at,
settled_at: settled_at,
- } as nwcTransaction
- })
+ } as nwcTransaction;
+ });
return {
result_type: "list_transactions",
result: {
- transactions: transactions
- }
- }
+ transactions: transactions,
+ },
+ };
},
// ––––---------- NWC Connection ––––----------
- replyNWC: async function (result: NWCResult | NWCError, event: NDKEvent, conn: NWCConnection) {
+ replyNWC: async function (
+ result: NWCResult | NWCError,
+ event: NDKEvent,
+ conn: NWCConnection,
+ ) {
// reply to NWC with result
let replyEvent = new NDKEvent(event.ndk);
replyEvent.kind = 23195;
- console.log("### replying with", JSON.stringify(result))
- replyEvent.content = await nip04.encrypt(conn.walletPrivateKey, event.author.pubkey, JSON.stringify(result));
- replyEvent.tags = [["p", event.author.pubkey], ["e", event.id]];
- console.log("### replyEvent", replyEvent)
- console.log("### replying to", event.id)
+ console.log("### replying with", JSON.stringify(result));
+ replyEvent.content = await nip04.encrypt(
+ conn.walletPrivateKey,
+ event.author.pubkey,
+ JSON.stringify(result),
+ );
+ replyEvent.tags = [
+ ["p", event.author.pubkey],
+ ["e", event.id],
+ ];
+ console.log("### replyEvent", replyEvent);
+ console.log("### replying to", event.id);
// await this.ndk.publish(replyEvent);
- await replyEvent.publish()
+ await replyEvent.publish();
},
- parseNWCCommand: async function (command: string, event: NDKEvent, conn: NWCConnection) {
+ parseNWCCommand: async function (
+ command: string,
+ event: NDKEvent,
+ conn: NWCConnection,
+ ) {
// parse command to JSON object {method: 'pay_invoice', params: {invoice: '1234'}}
- let nwcCommand: NWCCommand = JSON.parse(command)
- let result: NWCResult | NWCError
- console.log("### nwcCommand", nwcCommand)
+ let nwcCommand: NWCCommand = JSON.parse(command);
+ let result: NWCResult | NWCError;
+ console.log("### nwcCommand", nwcCommand);
// parse "get_info" without params
if (nwcCommand.method == "get_info") {
- result = await this.handleGetInfo(nwcCommand)
+ result = await this.handleGetInfo(nwcCommand);
} else if (nwcCommand.method == "get_balance") {
- result = await this.handleGetBalance(nwcCommand)
+ result = await this.handleGetBalance(nwcCommand);
} else if (nwcCommand.method == "pay_invoice") {
if (this.blocking) {
result = {
result_type: nwcCommand.method,
- error: { code: "INTERNAL", message: "Already processing a payment." }
- } as NWCError
+ error: {
+ code: "INTERNAL",
+ message: "Already processing a payment.",
+ },
+ } as NWCError;
}
- this.blocking = true
+ this.blocking = true;
try {
- result = await this.handlePayInvoice(nwcCommand)
+ result = await this.handlePayInvoice(nwcCommand);
} catch (e) {
- return
+ return;
} finally {
- this.blocking = false
+ this.blocking = false;
}
} else if (nwcCommand.method == "list_transactions") {
- result = await this.handleListTransactions(nwcCommand)
+ result = await this.handleListTransactions(nwcCommand);
} else {
- console.log("### method not supported", nwcCommand.method)
+ console.log("### method not supported", nwcCommand.method);
result = {
result_type: nwcCommand.method,
- error: { code: "NOT_IMPLEMENTED", message: "Method not supported" }
+ error: { code: "NOT_IMPLEMENTED", message: "Method not supported" },
} as NWCError;
}
- await this.replyNWC(result, event, conn)
+ await this.replyNWC(result, event, conn);
},
getConnectionString: function (connection: NWCConnection) {
- const walletPublicKeyHex = connection.walletPublicKey
- const connectionSecretHex = connection.connectionSecret
- return `nostr+walletconnect://${walletPublicKeyHex}?relay=${this.relays.join('&relay=')}&secret=${connectionSecretHex}`
+ const walletPublicKeyHex = connection.walletPublicKey;
+ const connectionSecretHex = connection.connectionSecret;
+ return `nostr+walletconnect://${walletPublicKeyHex}?relay=${this.relays.join("&relay=")}&secret=${connectionSecretHex}`;
},
generateNWCConnection: async function () {
- let conn: NWCConnection
+ let conn: NWCConnection;
// NOTE: we only support one connection for now
if (!this.connections.length) {
- const sk = generateSecretKey() // `sk` is a Uint8Array
- const walletPublicKeyHex = getPublicKey(sk) // `pk` is a hex string
- const walletPrivateKeyHex = bytesToHex(sk)
+ const sk = generateSecretKey(); // `sk` is a Uint8Array
+ const walletPublicKeyHex = getPublicKey(sk); // `pk` is a hex string
+ const walletPrivateKeyHex = bytesToHex(sk);
- const connectionSecret = generateSecretKey()
- const connectionPublicKeyHex = getPublicKey(connectionSecret)
- const connectionSecretHex = bytesToHex(connectionSecret)
+ const connectionSecret = generateSecretKey();
+ const connectionPublicKeyHex = getPublicKey(connectionSecret);
+ const connectionSecretHex = bytesToHex(connectionSecret);
conn = {
walletPublicKey: walletPublicKeyHex,
walletPrivateKey: walletPrivateKeyHex,
connectionSecret: connectionSecretHex,
connectionPublicKey: connectionPublicKeyHex,
- allowanceLeft: 1000
+ allowanceLeft: 1000,
} as NWCConnection;
- this.connections = this.connections.concat(conn)
+ this.connections = this.connections.concat(conn);
} else {
- conn = this.connections[0]
+ conn = this.connections[0];
}
- const walletSigner = new NDKPrivateKeySigner(conn.walletPrivateKey)
+ const walletSigner = new NDKPrivateKeySigner(conn.walletPrivateKey);
// close and delete all old subscriptions
- this.unsubscribeNWC()
- this.ndk = new NDK({ explicitRelayUrls: this.relays, signer: walletSigner });
+ this.unsubscribeNWC();
+ this.ndk = new NDK({
+ explicitRelayUrls: this.relays,
+ signer: walletSigner,
+ });
this.ndk.connect();
const nip47InfoEvent = new NDKEvent(this.ndk);
nip47InfoEvent.kind = NWCKind.NWCInfo;
- nip47InfoEvent.content = this.supportedMethods.join(' ')
+ nip47InfoEvent.content = this.supportedMethods.join(" ");
try {
// let's fetch the info event from the relay to see if we need to republish it
// use NWCKind.NWCInfo as an integer here
- let filterInfoEvent: NDKFilter = { kinds: [NWCKind.NWCInfo], authors: [conn.walletPublicKey] };
+ let filterInfoEvent: NDKFilter = {
+ kinds: [NWCKind.NWCInfo],
+ authors: [conn.walletPublicKey],
+ };
let eventsInfoEvent = await this.ndk.fetchEvents(filterInfoEvent);
if (eventsInfoEvent.size === 0) {
- await nip47InfoEvent.publish()
- console.log("### published nip47InfoEvent", nip47InfoEvent)
+ await nip47InfoEvent.publish();
+ console.log("### published nip47InfoEvent", nip47InfoEvent);
} else {
- console.log("### nip47InfoEvent already published")
+ console.log("### nip47InfoEvent already published");
}
} catch (e) {
- console.log("### could not publish nip47InfoEvent", nip47InfoEvent)
- console.log("### error", e)
+ console.log("### could not publish nip47InfoEvent", nip47InfoEvent);
+ console.log("### error", e);
}
},
listenToNWCCommands: async function () {
// if (!this.connections.length) {
// await this.generateNWCConnection()
// }
- await this.generateNWCConnection()
+ await this.generateNWCConnection();
// we only support one connection for now
- const conn = this.connections[0]
+ const conn = this.connections[0];
- const currentUnitTime = Math.floor(Date.now() / 1000)
+ const currentUnitTime = Math.floor(Date.now() / 1000);
let filter = {
kinds: [NWCKind.NWCRequest as NDKKind],
since: currentUnitTime,
authors: [conn.connectionPublicKey],
- "#p": [conn.walletPublicKey]
+ "#p": [conn.walletPublicKey],
} as NDKFilter;
const sub = this.ndk.subscribe(filter);
- console.log("### subscribing to NWC on relays: ", this.relays)
- this.subscriptions.push(sub)
+ console.log("### subscribing to NWC on relays: ", this.relays);
+ this.subscriptions.push(sub);
- sub.on("eose", () => console.log("All relays have reached the end of the event stream"));
+ sub.on("eose", () =>
+ console.log("All relays have reached the end of the event stream"),
+ );
sub.on("close", () => console.log("Subscription closed"));
sub.on("event", async (event) => {
@@ -351,25 +400,29 @@ export const useNWCStore = defineStore("nwc", {
// console.log("### event.tagValue('e')", event.tagValue("e"))
// console.log("### event.content", event.content)
if (event.kind != NWCKind.NWCRequest) {
- return // ignore non-NWC events
+ return; // ignore non-NWC events
}
if (!this.nwcEnabled) {
- console.log("### Received NWC command but NWC is disabled")
- return
+ console.log("### Received NWC command but NWC is disabled");
+ return;
}
- console.log("### NWC request!")
- console.log("### event", event)
- const decryptedContent = await nip04.decrypt(conn.connectionSecret, conn.walletPublicKey, event.content)
+ console.log("### NWC request!");
+ console.log("### event", event);
+ const decryptedContent = await nip04.decrypt(
+ conn.connectionSecret,
+ conn.walletPublicKey,
+ event.content,
+ );
// console.log("### decryptedContent", decryptedContent)
- await this.parseNWCCommand(decryptedContent, event, conn)
+ await this.parseNWCCommand(decryptedContent, event, conn);
});
},
unsubscribeNWC: function () {
- console.log("### unsubscribing from NWC")
+ console.log("### unsubscribing from NWC");
for (let sub of this.subscriptions) {
- sub.stop()
+ sub.stop();
}
- this.subscriptions = []
- }
+ this.subscriptions = [];
+ },
},
});
diff --git a/src/stores/p2pk.ts b/src/stores/p2pk.ts
index d2437ed2..b45fc9f6 100644
--- a/src/stores/p2pk.ts
+++ b/src/stores/p2pk.ts
@@ -1,44 +1,40 @@
import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core";
-import { generateSecretKey, getPublicKey } from 'nostr-tools'
-import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency
+import { generateSecretKey, getPublicKey } from "nostr-tools";
+import { bytesToHex } from "@noble/hashes/utils"; // already an installed dependency
import { WalletProof } from "stores/mints";
import token from "src/js/token";
type P2PKKey = {
- publicKey: string,
- privateKey: string,
- used: boolean,
- usedCount: number
-}
+ publicKey: string;
+ privateKey: string;
+ used: boolean;
+ usedCount: number;
+};
export const useP2PKStore = defineStore("p2pk", {
state: () => ({
p2pkKeys: useLocalStorage("cashu.P2PKKeys", []),
showP2PKDialog: false,
- showP2PKData: {} as P2PKKey
+ showP2PKData: {} as P2PKKey,
}),
- getters: {
-
- },
+ getters: {},
actions: {
haveThisKey: function (key: string) {
- return (
- this.p2pkKeys.filter((m) => m.publicKey == key).length > 0
- );
+ return this.p2pkKeys.filter((m) => m.publicKey == key).length > 0;
},
isValidPubkey: function (key: string) {
return key && key.length == 66;
},
setPrivateKeyUsed: function (key: string) {
- const thisKeys = this.p2pkKeys.filter((k) => k.privateKey == key)
+ const thisKeys = this.p2pkKeys.filter((k) => k.privateKey == key);
if (thisKeys.length) {
thisKeys[0].used = true;
thisKeys[0].usedCount += 1;
}
},
showKeyDetails: function (key: string) {
- const thisKeys = this.p2pkKeys.filter((k) => k.publicKey == key)
+ const thisKeys = this.p2pkKeys.filter((k) => k.publicKey == key);
if (thisKeys.length) {
this.showP2PKData = JSON.parse(JSON.stringify(thisKeys[0]));
this.showP2PKDialog = true;
@@ -46,21 +42,23 @@ export const useP2PKStore = defineStore("p2pk", {
},
showLastKey: function () {
if (this.p2pkKeys.length) {
- this.showP2PKData = JSON.parse(JSON.stringify(this.p2pkKeys[this.p2pkKeys.length - 1]));
+ this.showP2PKData = JSON.parse(
+ JSON.stringify(this.p2pkKeys[this.p2pkKeys.length - 1]),
+ );
this.showP2PKDialog = true;
}
},
generateKeypair: function () {
- let sk = generateSecretKey() // `sk` is a Uint8Array
- let pk = "02" + getPublicKey(sk) // `pk` is a hex string
- let skHex = bytesToHex(sk)
+ let sk = generateSecretKey(); // `sk` is a Uint8Array
+ let pk = "02" + getPublicKey(sk); // `pk` is a hex string
+ let skHex = bytesToHex(sk);
const keyPair: P2PKKey = {
publicKey: pk,
privateKey: skHex,
used: false,
- usedCount: 0
- }
- this.p2pkKeys = this.p2pkKeys.concat(keyPair)
+ usedCount: 0,
+ };
+ this.p2pkKeys = this.p2pkKeys.concat(keyPair);
},
getSecretP2PKPubkey: function (secret: string) {
try {
@@ -68,7 +66,7 @@ export const useP2PKStore = defineStore("p2pk", {
if (secretObject[0] == "P2PK" && secretObject[1]["data"] != undefined) {
return secretObject[1]["data"];
}
- } catch { }
+ } catch {}
return "";
},
isLocked: function (proofs: WalletProof[]) {
@@ -78,7 +76,7 @@ export const useP2PKStore = defineStore("p2pk", {
if (this.getSecretP2PKPubkey(secret)) {
return true;
}
- } catch { }
+ } catch {}
}
return false;
},
@@ -106,13 +104,11 @@ export const useP2PKStore = defineStore("p2pk", {
const pubkey = this.getSecretP2PKPubkey(secret);
if (pubkey && this.haveThisKey(pubkey)) {
// NOTE: we assume all tokens are locked to the same key here!
- return (
- this.p2pkKeys.filter((m) => m.publicKey == pubkey)[0].privateKey
- );
+ return this.p2pkKeys.filter((m) => m.publicKey == pubkey)[0]
+ .privateKey;
}
}
return "";
-
- }
+ },
},
});
diff --git a/src/stores/payment-request.ts b/src/stores/payment-request.ts
index d3097d92..384c9cf7 100644
--- a/src/stores/payment-request.ts
+++ b/src/stores/payment-request.ts
@@ -1,6 +1,12 @@
import { defineStore } from "pinia";
import { useWalletStore } from "./wallet";
-import { decodePaymentRequest, PaymentRequest, PaymentRequestPayload, PaymentRequestTransport, PaymentRequestTransportType } from "@cashu/cashu-ts";
+import {
+ decodePaymentRequest,
+ PaymentRequest,
+ PaymentRequestPayload,
+ PaymentRequestTransport,
+ PaymentRequestTransportType,
+} from "@cashu/cashu-ts";
import { useMintsStore } from "./mints";
import { useSendTokensStore } from "./sendTokensStore";
import { useNostrStore } from "./nostr";
@@ -9,15 +15,13 @@ import token from "src/js/token";
import { notify, notifyError, notifySuccess } from "src/js/notify";
import { useLocalStorage } from "@vueuse/core";
-
export const usePRStore = defineStore("payment-request", {
state: () => ({
showPRDialog: false,
showPRKData: "" as string,
enablePaymentRequest: useLocalStorage("cashu.pr.enable", false),
}),
- getters: {
- },
+ getters: {},
actions: {
newPaymentRequest(amount?: number, memo?: string) {
const walletStore = useWalletStore();
@@ -25,7 +29,7 @@ export const usePRStore = defineStore("payment-request", {
},
decodePaymentRequest(pr: string) {
console.log("decodePaymentRequest", pr);
- const request: PaymentRequest = decodePaymentRequest(pr)
+ const request: PaymentRequest = decodePaymentRequest(pr);
console.log("decodePaymentRequest", request);
// activate the mint in the payment request
if (request.mints && request.mints.length > 0) {
@@ -41,7 +45,9 @@ export const usePRStore = defineStore("payment-request", {
}
if (!foundMint) {
notifyError("We do not know the mint in the payment request");
- throw new Error(`We do not know the mint in the payment request: ${request.mints}`);
+ throw new Error(
+ `We do not know the mint in the payment request: ${request.mints}`,
+ );
}
}
@@ -73,7 +79,11 @@ export const usePRStore = defineStore("payment-request", {
}
}
},
- async payNostrPaymentRequest(request: PaymentRequest, transport: PaymentRequestTransport, tokenStr: string) {
+ async payNostrPaymentRequest(
+ request: PaymentRequest,
+ transport: PaymentRequestTransport,
+ tokenStr: string,
+ ) {
console.log("payNostrPaymentRequest", request, tokenStr);
console.log("transport", transport);
const nostrStore = useNostrStore();
@@ -92,14 +102,21 @@ export const usePRStore = defineStore("payment-request", {
};
const paymentPayloadString = JSON.stringify(paymentPayload);
try {
- await nostrStore.sendNip17DirectMessageToNprofile(transport.target, paymentPayloadString);
+ await nostrStore.sendNip17DirectMessageToNprofile(
+ transport.target,
+ paymentPayloadString,
+ );
} catch (error) {
console.error("Error paying payment request:", error);
notifyError("Could not pay request");
}
notifySuccess("Payment sent");
},
- async payPostPaymentRequest(request: PaymentRequest, transport: PaymentRequestTransport, tokenStr: string) {
+ async payPostPaymentRequest(
+ request: PaymentRequest,
+ transport: PaymentRequestTransport,
+ tokenStr: string,
+ ) {
console.log("payPostPaymentRequest", request, tokenStr);
// get the endpoint from the transport target and make an HTTP POST request with the paymentPayload as the body
const decodedToken = token.decode(tokenStr);
@@ -118,18 +135,16 @@ export const usePRStore = defineStore("payment-request", {
try {
const response = await fetch(transport.target, {
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
},
method: "POST",
body: paymentPayloadString,
});
notifySuccess("Payment sent");
-
} catch (error) {
console.error("Error paying payment request:", error);
notifyError("Could not pay request");
}
-
},
},
});
diff --git a/src/stores/proofs.ts b/src/stores/proofs.ts
index 72d88ce8..1fda1ba2 100644
--- a/src/stores/proofs.ts
+++ b/src/stores/proofs.ts
@@ -1,6 +1,12 @@
import { defineStore } from "pinia";
import { useMintsStore, WalletProof } from "./mints";
-import { Proof, getEncodedToken, getEncodedTokenV4, Token, TokenEntry } from "@cashu/cashu-ts";
+import {
+ Proof,
+ getEncodedToken,
+ getEncodedTokenV4,
+ Token,
+ TokenEntry,
+} from "@cashu/cashu-ts";
export const useProofsStore = defineStore("proofs", {
state: () => ({}),
@@ -19,8 +25,7 @@ export const useProofsStore = defineStore("proofs", {
mintStore.proofs
.filter((pr) => pr.secret === p.secret)
.forEach((pr) => (pr.reserved = reserved));
- }
- );
+ });
},
getUnreservedProofs: function (proofs: WalletProof[]) {
return proofs.filter((p) => !p.reserved);
@@ -31,14 +36,14 @@ export const useProofsStore = defineStore("proofs", {
let uniqueIds = [...new Set(proofs.map((p) => p.id))];
// keysets with these uniqueIds
let keysets = mintStore.mints.flatMap((m) =>
- m.keysets.filter((k) => uniqueIds.includes(k.id))
+ m.keysets.filter((k) => uniqueIds.includes(k.id)),
);
if (keysets.length === 0) {
throw new Error("No keysets found for proofs");
}
// mints that have any of the keyset.id
let mints = mintStore.mints.filter((m) =>
- m.keysets.some((k) => uniqueIds.includes(k.id))
+ m.keysets.some((k) => uniqueIds.includes(k.id)),
);
if (mints.length === 0) {
throw new Error("No mints found for proofs");
@@ -57,7 +62,6 @@ export const useProofsStore = defineStore("proofs", {
return getEncodedToken(token);
}
-
// // what we put into the JSON
// let mintsJson = mints.map((m) => [{ url: m.url, ids: m.keysets }][0]);
// let tokenV3 = {
@@ -72,7 +76,7 @@ export const useProofsStore = defineStore("proofs", {
let uniqueIds = [...new Set(proofs.map((p) => p.id))];
// mints that have any of the keyset IDs
let mints_keysets = mintStore.mints.filter((m) =>
- m.keysets.some((k) => uniqueIds.includes(k.id))
+ m.keysets.some((k) => uniqueIds.includes(k.id)),
);
// what we put into the JSON
let mints = mints_keysets.map((m) => [{ url: m.url, ids: m.keysets }][0]);
diff --git a/src/stores/restore.ts b/src/stores/restore.ts
index 12c8e341..a8876e0f 100644
--- a/src/stores/restore.ts
+++ b/src/stores/restore.ts
@@ -1,7 +1,7 @@
import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core";
-import { generateSecretKey, getPublicKey } from 'nostr-tools'
-import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency
+import { generateSecretKey, getPublicKey } from "nostr-tools";
+import { bytesToHex } from "@noble/hashes/utils"; // already an installed dependency
import { useWalletStore } from "./wallet";
import { CashuMint, CashuWallet, CheckStateEnum, Proof } from "@cashu/cashu-ts";
import { useMintsStore } from "./mints";
@@ -13,17 +13,21 @@ const MAX_GAP = 2;
export const useRestoreStore = defineStore("restore", {
state: () => ({
- showRestoreDialog: useLocalStorage("cashu.restore.showRestoreDialog", false),
+ showRestoreDialog: useLocalStorage(
+ "cashu.restore.showRestoreDialog",
+ false,
+ ),
restoringState: false,
restoringMint: "",
- mnemonicToRestore: useLocalStorage("cashu.restore.mnemonicToRestore", ""),
+ mnemonicToRestore: useLocalStorage(
+ "cashu.restore.mnemonicToRestore",
+ "",
+ ),
restoreProgress: 0,
restoreCounter: 0,
restoreStatus: "",
}),
- getters: {
-
- },
+ getters: {},
actions: {
restoreMint: async function (url: string) {
this.restoringState = true;
@@ -64,19 +68,26 @@ export const useRestoreStore = defineStore("restore", {
for (const keyset of keysets) {
console.log(`Restoring keyset ${keyset.id} with unit ${keyset.unit}`);
const bip39Seed = walletStore.mnemonicToSeedSync(mnemonic);
- const wallet = new CashuWallet(mint, { bip39seed: bip39Seed, unit: keyset.unit });
+ const wallet = new CashuWallet(mint, {
+ bip39seed: bip39Seed,
+ unit: keyset.unit,
+ });
let start = 0;
let emptyBatchCount = 0;
let restoreProofs: Proof[] = [];
while (emptyBatchCount < MAX_GAP) {
console.log(`Restoring proofs ${start} to ${start + BATCH_SIZE}`);
- const proofs = (await wallet.restore(start, BATCH_SIZE, { keysetId: keyset.id })).proofs;
+ const proofs = (
+ await wallet.restore(start, BATCH_SIZE, { keysetId: keyset.id })
+ ).proofs;
if (proofs.length === 0) {
console.log(`No proofs found for keyset ${keyset.id}`);
emptyBatchCount++;
} else {
- console.log(`> Restored ${proofs.length} proofs with sum ${proofs.reduce((s, p) => s + p.amount, 0)}`);
+ console.log(
+ `> Restored ${proofs.length} proofs with sum ${proofs.reduce((s, p) => s + p.amount, 0)}`,
+ );
restoreProofs = restoreProofs.concat(proofs);
emptyBatchCount = 0;
totalSteps += MAX_GAP * 2;
@@ -87,28 +98,39 @@ export const useRestoreStore = defineStore("restore", {
currentStep++;
this.restoreProgress = currentStep / totalSteps;
-
}
let restoredProofs: Proof[] = [];
for (let i = 0; i < restoreProofs.length; i += BATCH_SIZE) {
this.restoreStatus = `Checking proofs ${i} to ${i + BATCH_SIZE} for keyset ${keyset.id}`;
const checkRestoreProofs = restoreProofs.slice(i, i + BATCH_SIZE);
- const proofStates = await wallet.checkProofsStates(checkRestoreProofs);
- const spentProofs = checkRestoreProofs.filter((p, i) => proofStates[i].state === CheckStateEnum.SPENT);
+ const proofStates =
+ await wallet.checkProofsStates(checkRestoreProofs);
+ const spentProofs = checkRestoreProofs.filter(
+ (p, i) => proofStates[i].state === CheckStateEnum.SPENT,
+ );
const spentProofsSecrets = spentProofs.map((p) => p.secret);
- const unspentProofs = checkRestoreProofs.filter((p) => !spentProofsSecrets.includes(p.secret));
+ const unspentProofs = checkRestoreProofs.filter(
+ (p) => !spentProofsSecrets.includes(p.secret),
+ );
if (unspentProofs.length > 0) {
- console.log(`Found ${unspentProofs.length} unspent proofs with sum ${unspentProofs.reduce((s, p) => s + p.amount, 0)}`);
+ console.log(
+ `Found ${unspentProofs.length} unspent proofs with sum ${unspentProofs.reduce((s, p) => s + p.amount, 0)}`,
+ );
}
- const newProofs = unspentProofs.filter((p) => !mintStore.proofs.some((pr) => pr.secret === p.secret));
+ const newProofs = unspentProofs.filter(
+ (p) => !mintStore.proofs.some((pr) => pr.secret === p.secret),
+ );
mintStore.addProofs(newProofs);
restoredProofs = restoredProofs.concat(newProofs);
currentStep++;
this.restoreProgress = currentStep / totalSteps;
}
const restoredAmount = restoredProofs.reduce((s, p) => s + p.amount, 0);
- const restoredAmountStr = useUiStore().formatCurrency(restoredAmount, keyset.unit);
+ const restoredAmountStr = useUiStore().formatCurrency(
+ restoredAmount,
+ keyset.unit,
+ );
if (restoredAmount > 0) {
notifySuccess(`Restored ${restoredAmountStr}`);
restoredSomething = true;
diff --git a/src/stores/sendTokensStore.ts b/src/stores/sendTokensStore.ts
index 8f94c817..affe75b0 100644
--- a/src/stores/sendTokensStore.ts
+++ b/src/stores/sendTokensStore.ts
@@ -29,6 +29,6 @@ export const useSendTokensStore = defineStore("sendTokensStore", {
this.sendData.tokensBase64 = "";
this.sendData.p2pkPubkey = "";
this.sendData.paymentRequest = undefined;
- }
+ },
},
});
diff --git a/src/stores/settings.ts b/src/stores/settings.ts
index 1f2e0a5a..83fe9282 100644
--- a/src/stores/settings.ts
+++ b/src/stores/settings.ts
@@ -1,15 +1,31 @@
import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core";
-const defaultNostrRelays = ["wss://relay.damus.io", "wss://relay.8333.space/", "wss://nos.lol"]
+const defaultNostrRelays = [
+ "wss://relay.damus.io",
+ "wss://relay.8333.space/",
+ "wss://nos.lol",
+];
export const useSettingsStore = defineStore("settings", {
state: () => {
return {
- getBitcoinPrice: useLocalStorage("cashu.settings.getBitcoinPrice", false),
- checkSentTokens: useLocalStorage("cashu.settings.checkSentTokens", true),
- defaultNostrRelays: useLocalStorage("cashu.settings.defaultNostrRelays", defaultNostrRelays),
- includeFeesInSendAmount: useLocalStorage("cashu.settings.includeFeesInSendAmount", false),
- }
- }
+ getBitcoinPrice: useLocalStorage(
+ "cashu.settings.getBitcoinPrice",
+ false,
+ ),
+ checkSentTokens: useLocalStorage(
+ "cashu.settings.checkSentTokens",
+ true,
+ ),
+ defaultNostrRelays: useLocalStorage(
+ "cashu.settings.defaultNostrRelays",
+ defaultNostrRelays,
+ ),
+ includeFeesInSendAmount: useLocalStorage(
+ "cashu.settings.includeFeesInSendAmount",
+ false,
+ ),
+ };
+ },
});
diff --git a/src/stores/tokens.ts b/src/stores/tokens.ts
index 9564b51c..b87326f4 100644
--- a/src/stores/tokens.ts
+++ b/src/stores/tokens.ts
@@ -40,7 +40,7 @@ export const useTokensStore = defineStore("tokens", {
mint: string;
unit: string;
fee?: number;
- paymentRequest?: PaymentRequest
+ paymentRequest?: PaymentRequest;
}) {
this.historyTokens.push({
status: "paid",
@@ -79,15 +79,27 @@ export const useTokensStore = defineStore("tokens", {
paymentRequest,
});
},
- editHistoryToken(tokenToEdit: string, options?: { newAmount?: number; addAmount?: number, newStatus?: "paid" | "pending", newToken?: string, newFee?: number }): HistoryToken | undefined {
- const index = this.historyTokens.findIndex((t) => t.token === tokenToEdit);
+ editHistoryToken(
+ tokenToEdit: string,
+ options?: {
+ newAmount?: number;
+ addAmount?: number;
+ newStatus?: "paid" | "pending";
+ newToken?: string;
+ newFee?: number;
+ },
+ ): HistoryToken | undefined {
+ const index = this.historyTokens.findIndex(
+ (t) => t.token === tokenToEdit,
+ );
if (index >= 0) {
if (options) {
if (options.newToken) {
this.historyTokens[index].token = options.newToken;
}
if (options.newAmount) {
- this.historyTokens[index].amount = options.newAmount * Math.sign(this.historyTokens[index].amount);
+ this.historyTokens[index].amount =
+ options.newAmount * Math.sign(this.historyTokens[index].amount);
}
if (options.addAmount) {
if (this.historyTokens[index].amount > 0) {
@@ -95,7 +107,6 @@ export const useTokensStore = defineStore("tokens", {
} else {
this.historyTokens[index].amount -= options.addAmount;
}
-
}
if (options.newStatus) {
this.historyTokens[index].status = options.newStatus;
@@ -121,7 +132,7 @@ export const useTokensStore = defineStore("tokens", {
if (index >= 0) {
this.historyTokens.splice(index, 1);
}
- }
+ },
},
});
diff --git a/src/stores/ui.ts b/src/stores/ui.ts
index cf02305f..0df91b64 100644
--- a/src/stores/ui.ts
+++ b/src/stores/ui.ts
@@ -1,13 +1,19 @@
import { defineStore } from "pinia";
import { useMintsStore } from "./mints";
import { useLocalStorage } from "@vueuse/core";
-import { notifyApiError, notifyError, notifySuccess, notifyWarning, notify } from "../js/notify";
+import {
+ notifyApiError,
+ notifyError,
+ notifySuccess,
+ notifyWarning,
+ notify,
+} from "../js/notify";
const unitTickerShortMap = {
sat: "sats",
usd: "USD",
eur: "EUR",
- msat: "msats"
+ msat: "msats",
};
export const useUiStore = defineStore("ui", {
@@ -18,8 +24,7 @@ export const useUiStore = defineStore("ui", {
showSendDialog: false,
showReceiveDialog: false,
tab: useLocalStorage("cashu.ui.tab", "history" as string),
- expandHistory:
- useLocalStorage("cashu.ui.expandHistory", true as boolean),
+ expandHistory: useLocalStorage("cashu.ui.expandHistory", true as boolean),
globalMutexLock: false,
}),
actions: {
@@ -30,11 +35,11 @@ export const useUiStore = defineStore("ui", {
while (this.globalMutexLock) {
if (retries >= nRetries) {
- notify("Please try again.")
+ notify("Please try again.");
throw new Error("Failed to acquire global mutex lock");
}
retries++;
- await new Promise(resolve => setTimeout(resolve, retryInterval));
+ await new Promise((resolve) => setTimeout(resolve, retryInterval));
}
this.globalMutexLock = true;
@@ -52,7 +57,11 @@ export const useUiStore = defineStore("ui", {
fromMsat: function (value: number) {
return new Intl.NumberFormat(navigator.language).format(value) + " msat";
},
- formatCurrency: function (value: number, currency: string, showBalance = false) {
+ formatCurrency: function (
+ value: number,
+ currency: string,
+ showBalance = false,
+ ) {
if (currency == undefined) {
currency = "sat";
}
diff --git a/src/stores/wallet.ts b/src/stores/wallet.ts
index 71df679e..ee056a86 100644
--- a/src/stores/wallet.ts
+++ b/src/stores/wallet.ts
@@ -6,33 +6,53 @@ import { useProofsStore } from "./proofs";
import { useTokensStore } from "./tokens";
import { useReceiveTokensStore } from "./receiveTokensStore";
import { useUiStore } from "src/stores/ui";
-import { useP2PKStore } from "src/stores/p2pk"
-import { useSendTokensStore } from "src/stores/sendTokensStore"
+import { useP2PKStore } from "src/stores/p2pk";
+import { useSendTokensStore } from "src/stores/sendTokensStore";
import { usePRStore } from "./payment-request";
import * as _ from "underscore";
import token from "src/js/token";
-import { notifyApiError, notifyError, notifySuccess, notifyWarning, notify } from "src/js/notify";
-import { CashuMint, CashuWallet, Proof, MintQuotePayload, MeltQuotePayload, MeltQuoteResponse, CheckStateEnum, MeltQuoteState, MintQuoteState, PaymentRequest, PaymentRequestTransportType, PaymentRequestTransport, decodePaymentRequest } from "@cashu/cashu-ts";
-import { hashToCurve } from '@cashu/crypto/modules/common';
+import {
+ notifyApiError,
+ notifyError,
+ notifySuccess,
+ notifyWarning,
+ notify,
+} from "src/js/notify";
+import {
+ CashuMint,
+ CashuWallet,
+ Proof,
+ MintQuotePayload,
+ MeltQuotePayload,
+ MeltQuoteResponse,
+ CheckStateEnum,
+ MeltQuoteState,
+ MintQuoteState,
+ PaymentRequest,
+ PaymentRequestTransportType,
+ PaymentRequestTransport,
+ decodePaymentRequest,
+} from "@cashu/cashu-ts";
+import { hashToCurve } from "@cashu/crypto/modules/common";
import * as bolt11Decoder from "light-bolt11-decoder";
import { bech32 } from "bech32";
import axios from "axios";
import { date } from "quasar";
import { useNostrStore } from "./nostr";
-import { v4 as uuidv4 } from 'uuid';
+import { v4 as uuidv4 } from "uuid";
// bip39 requires Buffer
// import { Buffer } from 'buffer';
// window.Buffer = Buffer;
import { generateMnemonic, mnemonicToSeedSync } from "@scure/bip39";
-import { wordlist } from '@scure/bip39/wordlists/english';
+import { wordlist } from "@scure/bip39/wordlists/english";
// HACK: this is a workaround so that the catch block in the melt function does not throw an error when the user exits the app
// before the payment is completed. This is necessary because the catch block in the melt function would otherwise remove all
// quotes from the invoiceHistory and the user would not be able to pay the invoice again after reopening the app.
let isUnloading = false;
-window.addEventListener('beforeunload', () => {
+window.addEventListener("beforeunload", () => {
isUnloading = true;
});
@@ -65,10 +85,16 @@ export const useWalletStore = defineStore("wallet", {
mnemonic: useLocalStorage("cashu.mnemonic", ""),
invoiceHistory: useLocalStorage(
"cashu.invoiceHistory",
- [] as InvoiceHistory[]
+ [] as InvoiceHistory[],
+ ),
+ keysetCounters: useLocalStorage(
+ "cashu.keysetCounters",
+ [] as KeysetCounter[],
+ ),
+ oldMnemonicCounters: useLocalStorage(
+ "cashu.oldMnemonicCounters",
+ [] as { mnemonic: string; keysetCounters: KeysetCounter[] }[],
),
- keysetCounters: useLocalStorage("cashu.keysetCounters", [] as KeysetCounter[]),
- oldMnemonicCounters: useLocalStorage("cashu.oldMnemonicCounters", [] as { mnemonic: string, keysetCounters: KeysetCounter[] }[]),
invoiceData: {} as InvoiceHistory,
payInvoiceData: {
blocking: false,
@@ -90,7 +116,7 @@ export const useWalletStore = defineStore("wallet", {
sat: 0,
memo: "",
bolt11: "",
- } as { sat: number, memo: string, bolt11: string } | null,
+ } as { sat: number; memo: string; bolt11: string } | null,
lnurlpay: {
domain: "",
callback: "",
@@ -107,7 +133,12 @@ export const useWalletStore = defineStore("wallet", {
amount: null,
comment: "",
quote: "",
- } as { request: string, amount: number | null, comment: string, quote: string },
+ } as {
+ request: string;
+ amount: number | null;
+ comment: string;
+ quote: string;
+ },
},
};
},
@@ -120,12 +151,18 @@ export const useWalletStore = defineStore("wallet", {
}
const mnemonic: string = this.mnemonic;
const bip39Seed = mnemonicToSeedSync(mnemonic);
- const wallet = new CashuWallet(mint, { keys: mints.activeKeys, keysets: mints.activeKeysets, mintInfo: mints.activeInfo, bip39seed: bip39Seed, unit: mints.activeUnit });
+ const wallet = new CashuWallet(mint, {
+ keys: mints.activeKeys,
+ keysets: mints.activeKeysets,
+ mintInfo: mints.activeInfo,
+ bip39seed: bip39Seed,
+ unit: mints.activeUnit,
+ });
return wallet;
},
seed(): Uint8Array {
return mnemonicToSeedSync(this.mnemonic);
- }
+ },
},
actions: {
mnemonicToSeedSync: function (mnemonic: string): Uint8Array {
@@ -163,7 +200,9 @@ export const useWalletStore = defineStore("wallet", {
if (keysets == null || keysets.length == 0) {
throw new Error("no keysets found.");
}
- const unitKeysets = mintStore.activeMint().unitKeysets(mintStore.activeUnit)
+ const unitKeysets = mintStore
+ .activeMint()
+ .unitKeysets(mintStore.activeUnit);
if (unitKeysets == null || unitKeysets.length == 0) {
console.error("no keysets found for unit", mintStore.activeUnit);
throw new Error("no keysets found for unit");
@@ -175,17 +214,19 @@ export const useWalletStore = defineStore("wallet", {
// - order by id (whether it is hex or base64)
// - order by input_fee_ppk (ascending) TODO: this is not implemented yet
// - select the first one
- const activeKeysets = unitKeysets.filter(k => k.active)
- const hexKeysets = activeKeysets.filter(k => k.id.startsWith("00"))
- const base64Keysets = activeKeysets.filter(k => !k.id.startsWith("00"))
- const sortedKeysets = hexKeysets.concat(base64Keysets)
+ const activeKeysets = unitKeysets.filter((k) => k.active);
+ const hexKeysets = activeKeysets.filter((k) => k.id.startsWith("00"));
+ const base64Keysets = activeKeysets.filter((k) => !k.id.startsWith("00"));
+ const sortedKeysets = hexKeysets.concat(base64Keysets);
// const sortedKeysets = _.sortBy(activeKeysets, k => [k.id, k.input_fee_ppk])
if (sortedKeysets.length == 0) {
console.error("no active keysets found for unit", mintStore.activeUnit);
throw new Error("no active keysets found for unit");
}
const keyset_id = sortedKeysets[0].id;
- const keys = mintStore.activeMint().mint.keys.find((k) => k.id === keyset_id);
+ const keys = mintStore
+ .activeMint()
+ .mint.keys.find((k) => k.id === keyset_id);
// if (keys) {
// this.wallet.keys = keys;
// }
@@ -210,8 +251,11 @@ export const useWalletStore = defineStore("wallet", {
}
return chunks;
},
- coinSelectSpendBase64: function (proofs: WalletProof[], amount: number): WalletProof[] {
- const base64Proofs = proofs.filter(p => !p.id.startsWith("00"))
+ coinSelectSpendBase64: function (
+ proofs: WalletProof[],
+ amount: number,
+ ): WalletProof[] {
+ const base64Proofs = proofs.filter((p) => !p.id.startsWith("00"));
if (base64Proofs.length > 0) {
base64Proofs.sort((a, b) => b.amount - a.amount);
let sum = 0;
@@ -228,12 +272,20 @@ export const useWalletStore = defineStore("wallet", {
}
return [];
},
- coinSelect: function (proofs: WalletProof[], amount: number, includeFees: boolean = false): WalletProof[] {
+ coinSelect: function (
+ proofs: WalletProof[],
+ amount: number,
+ includeFees: boolean = false,
+ ): WalletProof[] {
if (proofs.reduce((s, t) => (s += t.amount), 0) < amount) {
// there are not enough proofs to pay the amount
return [];
}
- const { send: selectedProofs, keep: _ } = this.wallet.selectProofsToSend(proofs, amount, includeFees);
+ const { send: selectedProofs, keep: _ } = this.wallet.selectProofsToSend(
+ proofs,
+ amount,
+ includeFees,
+ );
const selectedWalletProofs = selectedProofs.map((p) => {
return { ...p, reserved: false } as WalletProof;
});
@@ -246,10 +298,10 @@ export const useWalletStore = defineStore("wallet", {
const spendableProofs = proofsStore.getUnreservedProofs(proofs);
if (proofsStore.sumProofs(spendableProofs) < amount) {
const balance = mintStore.activeMintBalance();
- const unit = mintStore.activeUnit
+ const unit = mintStore.activeUnit;
notifyWarning(
"Balance is too low",
- `${uIStore.formatCurrency(balance, unit)} is not enough to pay ${uIStore.formatCurrency(amount, unit)}.`
+ `${uIStore.formatCurrency(balance, unit)} is not enough to pay ${uIStore.formatCurrency(amount, unit)}.`,
);
throw Error("Balance too low");
}
@@ -258,10 +310,18 @@ export const useWalletStore = defineStore("wallet", {
getFeesForProofs: function (proofs: Proof[]): number {
return this.wallet.getFeesForProofs(proofs);
},
- sendToLock: async function (proofs: WalletProof[], amount: number, receiverPubkey: string) {
+ sendToLock: async function (
+ proofs: WalletProof[],
+ amount: number,
+ receiverPubkey: string,
+ ) {
const spendableProofs = this.spendableProofs(proofs, amount);
const proofsToSend = this.coinSelect(spendableProofs, amount);
- const { keep: keepProofs, send: sendProofs } = await this.wallet.send(amount, proofsToSend, { pubkey: receiverPubkey })
+ const { keep: keepProofs, send: sendProofs } = await this.wallet.send(
+ amount,
+ proofsToSend,
+ { pubkey: receiverPubkey },
+ );
const mintStore = useMintsStore();
// note: we do not store sendProofs in the proofs store but
// expect from the caller to store it in the history
@@ -269,7 +329,12 @@ export const useWalletStore = defineStore("wallet", {
mintStore.removeProofs(proofsToSend);
return { keepProofs, sendProofs };
},
- send: async function (proofs: WalletProof[], amount: number, invalidate: boolean = false, includeFees: boolean = false): Promise<{ keepProofs: Proof[], sendProofs: Proof[] }> {
+ send: async function (
+ proofs: WalletProof[],
+ amount: number,
+ invalidate: boolean = false,
+ includeFees: boolean = false,
+ ): Promise<{ keepProofs: Proof[]; sendProofs: Proof[] }> {
/*
splits proofs so the user can keep firstProofs, send scndProofs.
then sets scndProofs as reserved.
@@ -277,17 +342,19 @@ export const useWalletStore = defineStore("wallet", {
if invalidate, scndProofs (the one to send) are invalidated
*/
const mintStore = useMintsStore();
- const proofsStore = useProofsStore()
+ const proofsStore = useProofsStore();
const uIStore = useUiStore();
let proofsToSend: WalletProof[] = [];
- const keysetId = this.getKeyset()
+ const keysetId = this.getKeyset();
await uIStore.lockMutex();
try {
const spendableProofs = this.spendableProofs(proofs, amount);
proofsToSend = this.coinSelect(spendableProofs, amount, includeFees);
const totalAmount = proofsToSend.reduce((s, t) => (s += t.amount), 0);
- const fees = includeFees ? this.wallet.getFeesForProofs(proofsToSend) : 0;
+ const fees = includeFees
+ ? this.wallet.getFeesForProofs(proofsToSend)
+ : 0;
const targetAmount = amount + fees;
let keepProofs: Proof[] = [];
@@ -296,15 +363,21 @@ export const useWalletStore = defineStore("wallet", {
if (totalAmount != targetAmount) {
const counter = this.keysetCounter(keysetId);
proofsToSend = this.coinSelect(spendableProofs, targetAmount, true);
- ({ keep: keepProofs, send: sendProofs } = await this.wallet.send(targetAmount, proofsToSend, { counter, proofsWeHave: spendableProofs }));
- this.increaseKeysetCounter(keysetId, keepProofs.length + sendProofs.length);
+ ({ keep: keepProofs, send: sendProofs } = await this.wallet.send(
+ targetAmount,
+ proofsToSend,
+ { counter, proofsWeHave: spendableProofs },
+ ));
+ this.increaseKeysetCounter(
+ keysetId,
+ keepProofs.length + sendProofs.length,
+ );
mintStore.addProofs(keepProofs);
mintStore.addProofs(sendProofs);
mintStore.removeProofs(proofsToSend);
} else if (totalAmount == targetAmount) {
-
keepProofs = [];
- sendProofs = proofsToSend
+ sendProofs = proofsToSend;
} else {
throw new Error("could not split proofs.");
}
@@ -349,21 +422,29 @@ export const useWalletStore = defineStore("wallet", {
let proofs = token.getProofs(tokenJson);
// activate the mint and the unit
- await mintStore.activateMintUrl(token.getMint(tokenJson), false, false, tokenJson.unit);
+ await mintStore.activateMintUrl(
+ token.getMint(tokenJson),
+ false,
+ false,
+ tokenJson.unit,
+ );
const inputAmount = proofs.reduce((s, t) => (s += t.amount), 0);
await uIStore.lockMutex();
try {
// redeem
- const keysetId = this.getKeyset()
- const counter = this.keysetCounter(keysetId)
+ const keysetId = this.getKeyset();
+ const counter = this.keysetCounter(keysetId);
// const preference = this.outputAmountSelect(amount);
const privkey = receiveStore.receiveData.p2pkPrivateKey;
// const decodedToken = getDecodedToken(receiveStore.receiveData.tokensBase64);
// let tokenCts: Token
- let proofs: Proof[]
+ let proofs: Proof[];
try {
- proofs = await this.wallet.receive(receiveStore.receiveData.tokensBase64, { counter, privkey, proofsWeHave: mintStore.activeProofs })
+ proofs = await this.wallet.receive(
+ receiveStore.receiveData.tokensBase64,
+ { counter, privkey, proofsWeHave: mintStore.activeProofs },
+ );
this.increaseKeysetCounter(keysetId, proofs.length);
} catch (error: any) {
console.error(error);
@@ -378,11 +459,22 @@ export const useWalletStore = defineStore("wallet", {
const outputAmount = proofs.reduce((s, t) => (s += t.amount), 0);
// if token is already in history, set to paid, else add to history
- if (tokenStore.historyTokens.find((t) => t.token === receiveStore.receiveData.tokensBase64 && t.amount > 0)) {
+ if (
+ tokenStore.historyTokens.find(
+ (t) =>
+ t.token === receiveStore.receiveData.tokensBase64 && t.amount > 0,
+ )
+ ) {
tokenStore.setTokenPaid(receiveStore.receiveData.tokensBase64);
} else {
// if this is a self-sent token, we will find an outgoing token with the inverse amount
- if (tokenStore.historyTokens.find((t) => t.token === receiveStore.receiveData.tokensBase64 && t.amount < 0)) {
+ if (
+ tokenStore.historyTokens.find(
+ (t) =>
+ t.token === receiveStore.receiveData.tokensBase64 &&
+ t.amount < 0,
+ )
+ ) {
tokenStore.setTokenPaid(receiveStore.receiveData.tokensBase64);
}
const fee = inputAmount - outputAmount;
@@ -391,13 +483,15 @@ export const useWalletStore = defineStore("wallet", {
serializedProofs: receiveStore.receiveData.tokensBase64,
unit: mintStore.activeUnit,
mint: mintStore.activeMintUrl,
- fee: fee
+ fee: fee,
});
}
-
if (!!window.navigator.vibrate) navigator.vibrate(200);
- notifySuccess("Received " + uIStore.formatCurrency(outputAmount, mintStore.activeUnit));
+ notifySuccess(
+ "Received " +
+ uIStore.formatCurrency(outputAmount, mintStore.activeUnit),
+ );
} catch (error: any) {
console.error(error);
notifyApiError(error);
@@ -408,7 +502,6 @@ export const useWalletStore = defineStore("wallet", {
// }
},
-
// /mint
/**
* Ask the mint to generate an invoice for the given amount
@@ -426,11 +519,10 @@ export const useWalletStore = defineStore("wallet", {
try {
// create MintQuotePayload(this.invoiceData.amount) payload
const payload: MintQuotePayload = {
- amount: this.invoiceData.amount, unit: mintStore.activeUnit
+ amount: this.invoiceData.amount,
+ unit: mintStore.activeUnit,
};
- const data = await mintStore.activeMint().api.createMintQuote(
- payload
- );
+ const data = await mintStore.activeMint().api.createMintQuote(payload);
this.invoiceData.bolt11 = data.request;
this.invoiceData.quote = data.quote;
this.invoiceData.date = currentDateStr();
@@ -448,22 +540,32 @@ export const useWalletStore = defineStore("wallet", {
uIStore.unlockMutex();
}
},
- getMintQuoteState: async function (quote: string, mint: CashuMint): Promise {
+ getMintQuoteState: async function (
+ quote: string,
+ mint: CashuMint,
+ ): Promise {
// stateless function to check the state of a mint quote
const resp = await mint.checkMintQuote(quote);
return resp.state;
},
- getMeltQuoteState: async function (quote: string, mint: CashuMint): Promise {
+ getMeltQuoteState: async function (
+ quote: string,
+ mint: CashuMint,
+ ): Promise {
// stateless function to check the state of a melt quote
const resp = await mint.checkMeltQuote(quote);
return resp.state;
},
- mint: async function (amount: number, hash: string, verbose: boolean = true) {
+ mint: async function (
+ amount: number,
+ hash: string,
+ verbose: boolean = true,
+ ) {
const proofsStore = useProofsStore();
const mintStore = useMintsStore();
const tokenStore = useTokensStore();
const uIStore = useUiStore();
- const keysetId = this.getKeyset()
+ const keysetId = this.getKeyset();
await uIStore.lockMutex();
try {
// first we check if the mint quote is paid
@@ -476,8 +578,12 @@ export const useWalletStore = defineStore("wallet", {
}
throw new Error("invoice not paid yet.");
}
- const counter = this.keysetCounter(keysetId)
- const { proofs } = await this.wallet.mintProofs(amount, hash, { keysetId, counter, proofsWeHave: mintStore.activeProofs })
+ const counter = this.keysetCounter(keysetId);
+ const { proofs } = await this.wallet.mintProofs(amount, hash, {
+ keysetId,
+ counter,
+ proofsWeHave: mintStore.activeProofs,
+ });
this.increaseKeysetCounter(keysetId, proofs.length);
// const proofs = await this.mintApi(split, hash, verbose);
@@ -562,7 +668,11 @@ export const useWalletStore = defineStore("wallet", {
}
const invoice = this.payInvoiceData.invoice.bolt11;
// throw an error if the invoice is already in invoiceHistory
- if (this.invoiceHistory.find((i) => i.bolt11 === invoice && i.amount < 0 && i.status === "paid")) {
+ if (
+ this.invoiceHistory.find(
+ (i) => i.bolt11 === invoice && i.amount < 0 && i.status === "paid",
+ )
+ ) {
notifyError("Invoice already paid.");
throw new Error("invoice already paid.");
}
@@ -577,11 +687,11 @@ export const useWalletStore = defineStore("wallet", {
const keysetId = this.getKeyset();
let keysetCounterIncrease = 0;
-
// start melt
let sendProofs: Proof[] = [];
try {
- const { keepProofs: keepProofs, sendProofs: _sendProofs } = await this.send(mintStore.activeProofs, amount, false, true)
+ const { keepProofs: keepProofs, sendProofs: _sendProofs } =
+ await this.send(mintStore.activeProofs, amount, false, true);
sendProofs = _sendProofs;
if (sendProofs.length == 0) {
throw new Error("could not split proofs.");
@@ -592,10 +702,8 @@ export const useWalletStore = defineStore("wallet", {
throw error;
}
-
await uIStore.lockMutex();
try {
-
await this.addOutgoingPendingInvoiceToHistory(quote, sendProofs);
// NUT-08 blank outputs for change
@@ -613,15 +721,22 @@ export const useWalletStore = defineStore("wallet", {
// NOTE: if the user exits the app while we're in the API call, JS will emit an error that we would catch below!
// We have to handle that case in the catch block below
- const data = await this.wallet.meltProofs(quote, sendProofs, { keysetId, counter })
+ const data = await this.wallet.meltProofs(quote, sendProofs, {
+ keysetId,
+ counter,
+ });
if (data.quote.state != MeltQuoteState.PAID) {
throw new Error("Invoice not paid.");
}
- let amount_paid = amount - proofsStore.sumProofs(data.change)
+ let amount_paid = amount - proofsStore.sumProofs(data.change);
if (!!window.navigator.vibrate) navigator.vibrate(200);
- notifySuccess("Paid " + uIStore.formatCurrency(amount_paid, mintStore.activeUnit) + " via Lightning");
+ notifySuccess(
+ "Paid " +
+ uIStore.formatCurrency(amount_paid, mintStore.activeUnit) +
+ " via Lightning",
+ );
console.log("#### pay lightning: token paid");
// delete spent tokens from db
mintStore.removeProofs(sendProofs);
@@ -630,7 +745,7 @@ export const useWalletStore = defineStore("wallet", {
if (data.change != null) {
const changeProofs = data.change;
console.log(
- "## Received change: " + proofsStore.sumProofs(changeProofs)
+ "## Received change: " + proofsStore.sumProofs(changeProofs),
);
mintStore.addProofs(changeProofs);
}
@@ -642,7 +757,10 @@ export const useWalletStore = defineStore("wallet", {
mint: mintStore.activeMintUrl,
});
- this.updateInvoiceInHistory(quote, { status: "paid", amount: -amount_paid })
+ this.updateInvoiceInHistory(quote, {
+ status: "paid",
+ amount: -amount_paid,
+ });
this.payInvoiceData.invoice = { sat: 0, memo: "", bolt11: "" };
this.payInvoiceData.show = false;
@@ -654,15 +772,22 @@ export const useWalletStore = defineStore("wallet", {
throw error;
}
// get quote and check state
- const mintQuote = await mintStore.activeMint().api.checkMeltQuote(quote.quote);
- if (mintQuote.state == MeltQuoteState.PAID || mintQuote.state == MeltQuoteState.PENDING) {
- console.log("### melt: error, but quote is paid or pending. not rolling back.");
+ const mintQuote = await mintStore
+ .activeMint()
+ .api.checkMeltQuote(quote.quote);
+ if (
+ mintQuote.state == MeltQuoteState.PAID ||
+ mintQuote.state == MeltQuoteState.PENDING
+ ) {
+ console.log(
+ "### melt: error, but quote is paid or pending. not rolling back.",
+ );
throw error;
}
// roll back proof management and keyset counter
proofsStore.setReserved(sendProofs, false);
this.increaseKeysetCounter(keysetId, -keysetCounterIncrease);
- this.removeOutgoingInvoiceFromHistory(quote.quote)
+ this.removeOutgoingInvoiceFromHistory(quote.quote);
console.error(error);
this.handleOutputsHaveAlreadyBeenSignedError(keysetId, error);
@@ -675,13 +800,20 @@ export const useWalletStore = defineStore("wallet", {
getProofState: async function (proofs: Proof[]) {
const mintStore = useMintsStore();
const enc = new TextEncoder();
- const Ys = proofs.map((p) => hashToCurve(enc.encode(p.secret)).toHex(true));
+ const Ys = proofs.map((p) =>
+ hashToCurve(enc.encode(p.secret)).toHex(true),
+ );
const payload = { Ys: Ys };
- const { states } = await new CashuMint(mintStore.activeMintUrl).check(payload);
+ const { states } = await new CashuMint(mintStore.activeMintUrl).check(
+ payload,
+ );
return states;
},
// /check
- checkProofsSpendable: async function (proofs: Proof[], update_history = false) {
+ checkProofsSpendable: async function (
+ proofs: Proof[],
+ update_history = false,
+ ) {
/*
checks with the mint whether an array of proofs is still
spendable or already invalidated
@@ -695,8 +827,14 @@ export const useWalletStore = defineStore("wallet", {
const enc = new TextEncoder();
try {
const proofStates = await this.wallet.checkProofsStates(proofs);
- const spentProofsStates = proofStates.filter((p) => p.state == CheckStateEnum.SPENT)
- const spentProofs = proofs.filter((p) => spentProofsStates.find((s) => s.Y == hashToCurve(enc.encode(p.secret)).toHex(true)))
+ const spentProofsStates = proofStates.filter(
+ (p) => p.state == CheckStateEnum.SPENT,
+ );
+ const spentProofs = proofs.filter((p) =>
+ spentProofsStates.find(
+ (s) => s.Y == hashToCurve(enc.encode(p.secret)).toHex(true),
+ ),
+ );
if (spentProofs.length) {
mintStore.removeProofs(spentProofs);
@@ -722,7 +860,10 @@ export const useWalletStore = defineStore("wallet", {
throw error;
}
},
- checkTokenSpendable: async function (tokenStr: string, verbose: boolean = true) {
+ checkTokenSpendable: async function (
+ tokenStr: string,
+ verbose: boolean = true,
+ ) {
/*
checks whether a base64-encoded token (from the history table) has been spent already.
if it is spent, the appropraite entry in the history table is set to paid.
@@ -746,16 +887,27 @@ export const useWalletStore = defineStore("wallet", {
if (spentProofs != undefined && spentProofs.length == proofs.length) {
// all proofs are spent, set token to paid
tokenStore.setTokenPaid(tokenStr);
- } else if (spentProofs != undefined && spentProofs.length && spentProofs.length < proofs.length) {
+ } else if (
+ spentProofs != undefined &&
+ spentProofs.length &&
+ spentProofs.length < proofs.length
+ ) {
// not all proofs are spent, we remove the spent part of the token from the history
const spentAmount = proofsStore.sumProofs(spentProofs);
const serializedSpentProofs = proofsStore.serializeProofs(spentProofs);
- const unspentProofs = proofs.filter(p => !spentProofs.find(sp => sp.secret === p.secret))
+ const unspentProofs = proofs.filter(
+ (p) => !spentProofs.find((sp) => sp.secret === p.secret),
+ );
const unspentAmount = proofsStore.sumProofs(unspentProofs);
- const serializedUnspentProofs = proofsStore.serializeProofs(unspentProofs);
+ const serializedUnspentProofs =
+ proofsStore.serializeProofs(unspentProofs);
if (serializedSpentProofs && serializedUnspentProofs) {
- const historyToken = tokenStore.editHistoryToken(tokenStr, { newAmount: spentAmount, newStatus: "paid", newToken: serializedSpentProofs })
+ const historyToken = tokenStore.editHistoryToken(tokenStr, {
+ newAmount: spentAmount,
+ newStatus: "paid",
+ newToken: serializedSpentProofs,
+ });
// add all unspent proofs back to the history
// QUICK: we use the historyToken object here because we don't know if the transaction is incoming or outgoing (we don't know the sign of the amount)
if (historyToken) {
@@ -767,12 +919,17 @@ export const useWalletStore = defineStore("wallet", {
});
}
}
-
}
if (spentProofs != undefined && spentProofs.length) {
if (!!window.navigator.vibrate) navigator.vibrate(200);
const proofStore = useProofsStore();
- notifySuccess("Sent " + uIStore.formatCurrency(proofStore.sumProofs(spentProofs), mintStore.activeUnit));
+ notifySuccess(
+ "Sent " +
+ uIStore.formatCurrency(
+ proofStore.sumProofs(spentProofs),
+ mintStore.activeUnit,
+ ),
+ );
} else {
console.log("### token not paid yet");
if (verbose) {
@@ -791,7 +948,10 @@ export const useWalletStore = defineStore("wallet", {
}
try {
// check the state first
- const state = await this.getMintQuoteState(invoice.quote, new CashuMint(invoice.mint));
+ const state = await this.getMintQuoteState(
+ invoice.quote,
+ new CashuMint(invoice.mint),
+ );
if (state != MintQuoteState.PAID) {
console.log("### mintQuote not paid yet");
if (verbose) {
@@ -800,10 +960,19 @@ export const useWalletStore = defineStore("wallet", {
throw new Error("invoice not paid yet.");
}
// activate the mint
- await mintStore.activateMintUrl(invoice.mint, false, false, invoice.unit);
+ await mintStore.activateMintUrl(
+ invoice.mint,
+ false,
+ false,
+ invoice.unit,
+ );
const proofs = await this.mint(invoice.amount, invoice.quote, verbose);
if (!!window.navigator.vibrate) navigator.vibrate(200);
- notifySuccess("Received " + uIStore.formatCurrency(invoice.amount, mintStore.activeUnit) + " via Lightning");
+ notifySuccess(
+ "Received " +
+ uIStore.formatCurrency(invoice.amount, mintStore.activeUnit) +
+ " via Lightning",
+ );
return proofs;
} catch (error) {
if (verbose) {
@@ -843,20 +1012,30 @@ export const useWalletStore = defineStore("wallet", {
notify("Invoice still pending");
}
throw new Error("invoice not paid yet.");
- }
- else if (mintQuote.state == MeltQuoteState.UNPAID) {
+ } else if (mintQuote.state == MeltQuoteState.UNPAID) {
// we assume that the payment failed and we unset the proofs as reserved
useProofsStore().setReserved(proofs, false);
this.removeOutgoingInvoiceFromHistory(quote);
notifyWarning("Lightning payment failed");
} else if (mintQuote.state == MeltQuoteState.PAID) {
// if the invoice is paid, we check if all proofs are spent and if so, we invalidate them and set the invoice state in the history to "paid"
- await mintStore.activateMintUrl(invoice.mint, false, false, invoice.unit);
+ await mintStore.activateMintUrl(
+ invoice.mint,
+ false,
+ false,
+ invoice.unit,
+ );
const spentProofs = await this.checkProofsSpendable(proofs, true);
if (spentProofs != undefined && spentProofs.length == proofs.length) {
mintStore.removeProofs(proofs);
if (!!window.navigator.vibrate) navigator.vibrate(200);
- notifySuccess("Sent " + uIStore.formatCurrency(useProofsStore().sumProofs(proofs), mintStore.activeUnit));
+ notifySuccess(
+ "Sent " +
+ uIStore.formatCurrency(
+ useProofsStore().sumProofs(proofs),
+ mintStore.activeUnit,
+ ),
+ );
}
// set invoice in history to paid
this.setInvoicePaid(quote);
@@ -870,7 +1049,10 @@ export const useWalletStore = defineStore("wallet", {
}
},
////////////// UI HELPERS //////////////
- addOutgoingPendingInvoiceToHistory: function (quote: MeltQuoteResponse, sendProofs: Proof[]) {
+ addOutgoingPendingInvoiceToHistory: function (
+ quote: MeltQuoteResponse,
+ sendProofs: Proof[],
+ ) {
const proofsStore = useProofsStore();
const serlializedToken = proofsStore.serializeProofs(sendProofs);
proofsStore.setReserved(sendProofs, true);
@@ -893,18 +1075,22 @@ export const useWalletStore = defineStore("wallet", {
this.invoiceHistory.splice(index, 1);
}
},
- updateInvoiceInHistory: function (quote: MeltQuoteResponse, options?: { status?: "pending" | "paid", amount?: number }) {
- this.invoiceHistory.filter((i) => i.quote === quote.quote).forEach((i) => {
- if (options) {
- if (options.status) {
- i.status = options.status;
- }
- if (options.amount) {
- i.amount = options.amount;
+ updateInvoiceInHistory: function (
+ quote: MeltQuoteResponse,
+ options?: { status?: "pending" | "paid"; amount?: number },
+ ) {
+ this.invoiceHistory
+ .filter((i) => i.quote === quote.quote)
+ .forEach((i) => {
+ if (options) {
+ if (options.status) {
+ i.status = options.status;
+ }
+ if (options.amount) {
+ i.amount = options.amount;
+ }
}
- }
- }
- );
+ });
},
// checkPendingInvoices: async function (verbose: boolean = true) {
// const last_n = 10;
@@ -935,7 +1121,7 @@ export const useWalletStore = defineStore("wallet", {
break;
}
if (t.status === "pending" && t.amount < 0) {
- console.log("### checkPendingTokens", t.token)
+ console.log("### checkPendingTokens", t.token);
this.checkTokenSpendable(t.token, verbose);
i += 1;
}
@@ -977,11 +1163,11 @@ export const useWalletStore = defineStore("wallet", {
cleanInvoice.timestamp = tag.value;
} else if (tag.name === "expiry") {
var expireDate = new Date(
- (cleanInvoice.timestamp + tag.value) * 1000
+ (cleanInvoice.timestamp + tag.value) * 1000,
);
cleanInvoice.expireDate = date.formatDate(
expireDate,
- "YYYY-MM-DDTHH:mm:ss.SSSZ"
+ "YYYY-MM-DDTHH:mm:ss.SSSZ",
);
cleanInvoice.expired = false; // TODO
}
@@ -990,31 +1176,31 @@ export const useWalletStore = defineStore("wallet", {
this.payInvoiceData.invoice = Object.freeze(cleanInvoice);
// get quote for this request
- await this.meltQuote()
+ await this.meltQuote();
},
handleCashuToken: function () {
this.payInvoiceData.show = false;
receiveStore.showReceiveTokens = true;
},
handleP2PK: function (req: string) {
- const sendTokenStore = useSendTokensStore()
- sendTokenStore.sendData.p2pkPubkey = req
- sendTokenStore.showSendTokens = true
- sendTokenStore.showLockInput = true
+ const sendTokenStore = useSendTokensStore();
+ sendTokenStore.sendData.p2pkPubkey = req;
+ sendTokenStore.showSendTokens = true;
+ sendTokenStore.showLockInput = true;
},
handlePaymentRequest: function (req: string) {
- const prStore = usePRStore()
- prStore.decodePaymentRequest(req)
+ const prStore = usePRStore();
+ prStore.decodePaymentRequest(req);
},
decodeRequest: async function (req: string) {
- const p2pkStore = useP2PKStore()
- this.payInvoiceData.input.request = req
+ const p2pkStore = useP2PKStore();
+ this.payInvoiceData.input.request = req;
if (req.toLowerCase().startsWith("lnbc")) {
this.payInvoiceData.input.request = req;
- await this.handleBolt11Invoice()
+ await this.handleBolt11Invoice();
} else if (req.toLowerCase().startsWith("lightning:")) {
this.payInvoiceData.input.request = req.slice(10);
- await this.handleBolt11Invoice()
+ await this.handleBolt11Invoice();
} else if (req.toLowerCase().startsWith("lnurl:")) {
this.payInvoiceData.input.request = req.slice(6);
await this.lnurlPayFirst(this.payInvoiceData.input.request);
@@ -1027,35 +1213,37 @@ export const useWalletStore = defineStore("wallet", {
req.toLowerCase().startsWith("lnurl1") ||
req.match(/[\w.+-~_]+@[\w.+-~_]/)
) {
- this.payInvoiceData.input.request = req
+ this.payInvoiceData.input.request = req;
await this.lnurlPayFirst(this.payInvoiceData.input.request);
} else if (req.indexOf("cashuA") !== -1) {
// very dirty way of parsing cashu tokens from either a pasted token or a URL like https://host.com?token=eyJwcm
- receiveStore.receiveData.tokensBase64 = req.slice(req.indexOf("cashuA"));
- this.handleCashuToken()
+ receiveStore.receiveData.tokensBase64 = req.slice(
+ req.indexOf("cashuA"),
+ );
+ this.handleCashuToken();
} else if (req.indexOf("cashuB") !== -1) {
- receiveStore.receiveData.tokensBase64 = req.slice(req.indexOf("cashuB"));
- this.handleCashuToken()
+ receiveStore.receiveData.tokensBase64 = req.slice(
+ req.indexOf("cashuB"),
+ );
+ this.handleCashuToken();
} else if (p2pkStore.isValidPubkey(req)) {
- this.handleP2PK(req)
- } else if (
- req.startsWith("http")
- ) {
+ this.handleP2PK(req);
+ } else if (req.startsWith("http")) {
const mintStore = useMintsStore();
- mintStore.addMintData = { url: req, nickname: "" }
+ mintStore.addMintData = { url: req, nickname: "" };
} else if (req.startsWith("creqA")) {
- await this.handlePaymentRequest(req)
+ await this.handlePaymentRequest(req);
}
},
fetchBitcoinPriceUSD: async function () {
var { data } = await axios.get(
- "https://api.coinbase.com/v2/exchange-rates?currency=BTC"
+ "https://api.coinbase.com/v2/exchange-rates?currency=BTC",
);
return data.data.rates.USD;
},
lnurlPayFirst: async function (address: string) {
var host;
- var data
+ var data;
if (address.split("@").length == 2) {
let [user, lnaddresshost] = address.split("@");
host = `https://${lnaddresshost}/.well-known/lnurlp/${user}`;
@@ -1076,7 +1264,9 @@ export const useWalletStore = defineStore("wallet", {
}
if (data.tag == "payRequest") {
this.payInvoiceData.lnurlpay = data;
- this.payInvoiceData.lnurlpay.domain = host.split("https://")[1].split("/")[0];
+ this.payInvoiceData.lnurlpay.domain = host
+ .split("https://")[1]
+ .split("/")[0];
if (
this.payInvoiceData.lnurlpay.maxSendable ==
this.payInvoiceData.lnurlpay.minSendable
@@ -1116,7 +1306,7 @@ export const useWalletStore = defineStore("wallet", {
} catch (e) {
notifyError(
"Couldn't get Bitcoin price",
- "fetchBitcoinPriceUSD Error"
+ "fetchBitcoinPriceUSD Error",
);
return;
}
@@ -1126,7 +1316,7 @@ export const useWalletStore = defineStore("wallet", {
amount = Math.floor(usdAmount * satPrice);
}
var { data } = await axios.get(
- `${this.payInvoiceData.lnurlpay.callback}?amount=${amount * 1000}`
+ `${this.payInvoiceData.lnurlpay.callback}?amount=${amount * 1000}`,
);
// check http error
if (data.status == "ERROR") {
@@ -1140,9 +1330,12 @@ export const useWalletStore = defineStore("wallet", {
if (this.mnemonic == "") {
this.mnemonic = generateMnemonic(wordlist);
}
- return this.mnemonic
+ return this.mnemonic;
},
- handleOutputsHaveAlreadyBeenSignedError: function (keysetId: string, error: any) {
+ handleOutputsHaveAlreadyBeenSignedError: function (
+ keysetId: string,
+ error: any,
+ ) {
if (error.message.includes("outputs have already been signed")) {
this.increaseKeysetCounter(keysetId, 10);
notify("Please try again.");
@@ -1154,11 +1347,13 @@ export const useWalletStore = defineStore("wallet", {
const nostrStore = useNostrStore();
const mintStore = useMintsStore();
const tags = [["n", "17"]];
- const transport = [{
- type: PaymentRequestTransportType.NOSTR,
- target: nostrStore.nprofile,
- tags: tags,
- }] as PaymentRequestTransport[];
+ const transport = [
+ {
+ type: PaymentRequestTransportType.NOSTR,
+ target: nostrStore.nprofile,
+ tags: tags,
+ },
+ ] as PaymentRequestTransport[];
const uuid = uuidv4().split("-")[0];
const paymentRequest = new PaymentRequest(
transport,
@@ -1171,10 +1366,12 @@ export const useWalletStore = defineStore("wallet", {
// TMP
console.log("### paymentRequest", paymentRequest.toEncodedRequest());
- const request: PaymentRequest = decodePaymentRequest(paymentRequest.toEncodedRequest())
- console.log('### decoded paymentRequest', request);
+ const request: PaymentRequest = decodePaymentRequest(
+ paymentRequest.toEncodedRequest(),
+ );
+ console.log("### decoded paymentRequest", request);
return paymentRequest.toEncodedRequest();
- }
+ },
},
});
diff --git a/src/stores/workers.ts b/src/stores/workers.ts
index b5ae0f7a..cdd7ee32 100644
--- a/src/stores/workers.ts
+++ b/src/stores/workers.ts
@@ -78,10 +78,7 @@ export const useWorkersStore = defineStore("workers", {
this.clearAllWorkers();
}
console.log("### checkTokenSpendableWorker setInterval", nInterval);
- let paid = await walletStore.checkTokenSpendable(
- tokensBase64,
- false
- );
+ let paid = await walletStore.checkTokenSpendable(tokensBase64, false);
if (paid) {
console.log("### stopping token check worker");
this.clearAllWorkers();