From ca1bb86036106be2f554d6f3940aad773ea8b258 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 9 Mar 2023 09:57:14 +0000 Subject: [PATCH] wip --- packages/app/package.json | 1 + packages/app/src/Const.ts | 6 ++++ packages/app/src/Element/Text.tsx | 39 +++++++++++++++++++++++++- packages/app/src/Wallet/Cashu.ts | 46 +++++++++++++++++++++++++++++++ yarn.lock | 44 ++++++++++++++++++++++++++++- 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 packages/app/src/Wallet/Cashu.ts diff --git a/packages/app/package.json b/packages/app/package.json index c060da625..af8475d17 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -6,6 +6,7 @@ "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1", "@fortawesome/react-fontawesome": "^0.2.0", + "@gandlaf21/cashu-ts": "^0.6.0", "@jukben/emoji-search": "^2.0.1", "@lightninglabs/lnc-web": "^0.2.3-alpha", "@noble/hashes": "^1.2.0", diff --git a/packages/app/src/Const.ts b/packages/app/src/Const.ts index 582ef2b03..a4582d6c5 100644 --- a/packages/app/src/Const.ts +++ b/packages/app/src/Const.ts @@ -179,3 +179,9 @@ export const MagnetRegex = /(magnet:[\S]+)/i; */ export const WavlakeRegex = /https?:\/\/(?:player\.|www\.)?wavlake\.com\/(?!top|new|artists|account|activity|login|preferences|feed)(?:(?:track|album)\/[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}|[a-z-]+)/i; +/player\.wavlake\.com\/(?!feed\/)(track\/[.a-zA-Z0-9-]+|album\/[.a-zA-Z0-9-]+|[.a-zA-Z0-9-]+)/i; + +/* + * Regex to match any base64 string + */ +export const Base64Regex = /([a-zA-Z0-9+/]+={,2})/; diff --git a/packages/app/src/Element/Text.tsx b/packages/app/src/Element/Text.tsx index 6a173bab6..82073bdd7 100644 --- a/packages/app/src/Element/Text.tsx +++ b/packages/app/src/Element/Text.tsx @@ -4,9 +4,10 @@ import { Link, useLocation } from "react-router-dom"; import ReactMarkdown from "react-markdown"; import { visit, SKIP } from "unist-util-visit"; import * as unist from "unist"; +import base64 from "@protobufjs/base64"; import { HexKey, NostrPrefix } from "@snort/nostr"; -import { MentionRegex, InvoiceRegex, HashtagRegex } from "Const"; +import { MentionRegex, InvoiceRegex, HashtagRegex, Base64Regex } from "Const"; import { eventLink, hexToBech32, splitByUrl, unwrap, validateNostrLink } from "Util"; import Invoice from "Element/Invoice"; import Hashtag from "Element/Hashtag"; @@ -68,6 +69,41 @@ export default function Text({ content, tags, creator, disableMedia, depth }: Te .flat(); } + function extractBase64Strings(fragments: Fragment[]) { + return fragments + .map(f => { + if (typeof f === "string") { + return f.split(Base64Regex).map(a => { + const parsed = base64.test(a); + if (parsed && a.length > 0) { + const buff = new Uint8Array(3 * (a.length / 4)); + const len = base64.decode(a, buff, 0); + const text = new TextDecoder().decode(buff.slice(0, len)); + if (text.startsWith("{") && text.endsWith("}") && text.includes("proofs")) { + const data = JSON.parse(text) as { + proofs: [ + { + amount: number; + } + ]; + }; + return ( +
+

Cashu tokens

+

Amount: {data.proofs.reduce((acc, v) => (acc += v.amount), 0)} sats

+
+ ); + } + return a; + } + return a; + }); + } + return f; + }) + .flat(); + } + function extractMentions(frag: TextFragment) { return frag.body .map(f => { @@ -163,6 +199,7 @@ export default function Text({ content, tags, creator, disableMedia, depth }: Te fragments = extractLinks(fragments); fragments = extractInvoices(fragments); fragments = extractHashtags(fragments); + fragments = extractBase64Strings(fragments); return fragments; } diff --git a/packages/app/src/Wallet/Cashu.ts b/packages/app/src/Wallet/Cashu.ts new file mode 100644 index 000000000..b97cff758 --- /dev/null +++ b/packages/app/src/Wallet/Cashu.ts @@ -0,0 +1,46 @@ +import { CashuMint, CashuWallet as TheCashuWallet, getEncodedProofs, Proof } from "@gandlaf21/cashu-ts"; + +import { InvoiceRequest, LNWallet, WalletInfo, WalletInvoice } from "Wallet"; + +export class CashuWallet implements LNWallet { + #mint: string; + #walletPath: string; + #wallet?: TheCashuWallet; + + constructor(mint: string, path: string) { + this.#mint = mint; + this.#walletPath = path; + } + + isReady(): boolean { + return this.#wallet !== undefined; + } + + getInfo: () => Promise; + + async login(_?: string | undefined): Promise { + const m = new CashuMint(this.#mint, this.#walletPath); + const keys = await m.getKeys(); + this.#wallet = new TheCashuWallet(keys, m); + return true; + } + + close: () => Promise; + getBalance: () => Promise; + createInvoice: (req: InvoiceRequest) => Promise; + payInvoice: (pr: string) => Promise; + getInvoices: () => Promise; +} + +interface NutBank { + proofs: Array; +} + +export interface NutStashBackup { + proofs: Array; + mints: [ + { + mintURL: string; + } + ]; +} diff --git a/yarn.lock b/yarn.lock index 501f71111..a3b483827 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1345,6 +1345,25 @@ dependencies: prop-types "^15.8.1" +"@gandlaf21/bolt11-decode@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@gandlaf21/bolt11-decode/-/bolt11-decode-3.0.6.tgz#ab514fdcee596ccffbfef7b33bc9cc93afe386ee" + integrity sha512-KUcAK2b9or8J47hzNTM2A+xdU0jCGIL4oC4TDyUlRYMfS5dBVOh4ywg9r3TZD8C/eVx7r14Hp4F79CSDjyCWTQ== + dependencies: + bech32 "^1.1.2" + bn.js "^4.11.8" + buffer "^6.0.3" + +"@gandlaf21/cashu-ts@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@gandlaf21/cashu-ts/-/cashu-ts-0.6.0.tgz#ab665440bf8afc984b99cbd097468e8ad4263ca8" + integrity sha512-5K46HrdIiWXwzTkCq+qM50KOSNjWnHIKz+aZFH0X6dVgTNNYUWP/ucBxiOuHKs71VfjqWmDl0KqSAptKdclkEQ== + dependencies: + "@gandlaf21/bolt11-decode" "^3.0.6" + "@noble/secp256k1" "^1.7.0" + axios "^1.2.1" + buffer "^6.0.3" + "@humanwhocodes/config-array@^0.11.8": version "0.11.8" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" @@ -3108,6 +3127,15 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== +axios@^1.2.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" @@ -5192,7 +5220,7 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.0.0: +follow-redirects@^1.0.0, follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -5232,6 +5260,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -8658,6 +8695,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"