From a77147d07779ba63f545685368a2e72d2779b976 Mon Sep 17 00:00:00 2001 From: Daniel Gonzalez Date: Wed, 21 May 2025 14:59:10 -0400 Subject: [PATCH] feat(nostr): Generate Nostr nsec and npub --- package-lock.json | 67 ++++++++++++++++- package.json | 1 + .../app-body/bch-wallet/wallet-summary.js | 72 ++++++++++++++++++- 3 files changed, 136 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2fa5c69..5e67dc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@fortawesome/free-regular-svg-icons": "6.7.2", "@fortawesome/free-solid-svg-icons": "6.7.2", "@fortawesome/react-fontawesome": "0.2.2", + "@noble/hashes": "^1.8.0", "axios": "0.27.2", "bch-dex-lib": "2.0.2", "bch-message-lib": "2.2.1", @@ -1929,6 +1930,18 @@ "nostr-wasm": "0.1.0" } }, + "node_modules/@chris.troutner/nostr-tools/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@chris.troutner/react-jdenticon": { "version": "1.0.1", "license": "MIT", @@ -2976,10 +2989,12 @@ "license": "MIT" }, "node_modules/@noble/hashes": { - "version": "1.3.1", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", "license": "MIT", "engines": { - "node": ">= 16" + "node": "^14.21.3 || >=16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -3657,6 +3672,30 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/bip32/node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@scure/bip39": { "version": "1.2.1", "license": "MIT", @@ -3668,6 +3707,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "http://94.130.170.209:4873/@sec-ant%2freadable-stream/-/readable-stream-0.4.1.tgz", @@ -15150,6 +15201,18 @@ } } }, + "node_modules/nostr-tools/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/nostr-wasm": { "version": "0.1.0", "license": "MIT", diff --git a/package.json b/package.json index 883e434..7344823 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@fortawesome/free-regular-svg-icons": "6.7.2", "@fortawesome/free-solid-svg-icons": "6.7.2", "@fortawesome/react-fontawesome": "0.2.2", + "@noble/hashes": "^1.8.0", "axios": "0.27.2", "bch-dex-lib": "2.0.2", "bch-message-lib": "2.2.1", diff --git a/src/components/app-body/bch-wallet/wallet-summary.js b/src/components/app-body/bch-wallet/wallet-summary.js index 998a4dc..470d8ce 100644 --- a/src/components/app-body/bch-wallet/wallet-summary.js +++ b/src/components/app-body/bch-wallet/wallet-summary.js @@ -3,7 +3,7 @@ */ // Global npm libraries -import React, { useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { Container, Row, Col, Card } from 'react-bootstrap' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faWallet, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons' @@ -12,17 +12,26 @@ import { faWallet, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons' // Local libraries import './wallet-summary.css' import CopyOnClick from './copy-on-click' +import { getPublicKey } from 'nostr-tools/pure' +import { base58_to_binary as base58ToBinary } from 'base58-js' +import { bytesToHex } from '@noble/hashes/utils' // already an installed dependency function WalletSummary (props) { // Props const appData = props.appData - const bchWalletState = appData.bchWalletState + console.log('wallet summary state: ', bchWalletState) // State const [blurredMnemonic, setBlurredMnemonic] = useState(true) const [blurredPrivateKey, setBlurredPrivateKey] = useState(true) + const [blurredNostrPrivKey, setBlurredNostrPrivKey] = useState(true) + + const [nostrKeyPair, setNostrKeyPair] = useState({ + privHex: '', + pubHex: '' + }) // Encapsulate component state into an object that can be passed to child functions const walletSummaryData = { @@ -64,6 +73,39 @@ function WalletSummary (props) { } } + // Toggle the state of blurring for the private key + const toggleNostrPrivateKeyBlur = (inObj = {}) => { + try { + setBlurredNostrPrivKey(!blurredNostrPrivKey) + } catch (error) { + console.error('Error toggling private key blur: ', error) + } + } + + const nostrKeyPairFromWIF = useCallback((WIF) => { + if (!WIF) return + + // Extract the privaty key from the WIF, using this guide: + // https://learnmeabitcoin.com/technical/keys/private-key/wif/ + const wifBuf = base58ToBinary(WIF) + const privBuf = wifBuf.slice(1, 33) + // console.log('privBuf: ', privBuf) + + const privHex = bytesToHex(privBuf) + console.log('BCH & Nostr private key (HEX format): ', privHex) + + const pubHex = getPublicKey(privBuf) + console.log('nostrPubKey: ', pubHex) + + return { + privHex, + pubHex + } + }, []) + + useEffect(() => { + setNostrKeyPair(nostrKeyPairFromWIF(bchWalletState.privateKey)) + }, [nostrKeyPairFromWIF, bchWalletState]) return ( <> @@ -151,6 +193,32 @@ function WalletSummary (props) { + + + Nostr Priv Key: {nostrKeyPair.privHex} + + + toggleNostrPrivateKeyBlur(nostrKeyPair.privHex)} + /> + + + + + + + + Nostr Pub Key: {nostrKeyPair.pubHex} + + + + + + +